Question
· Apr 20, 2018

Properties with underscore in BPL

Hello.

We have a request message with properties that has underscore. This is because we need to convert that request to JSON.

The problem is when we try to assign a value in a Business Process with the editor. It doesn't work, it doesn't compile.

How can we fix this?

This is the object used in the request: 

Class test.msg.struct.TestXML Extends (%SerialObject,%XML.Adaptor)
{
Property "status_id" As %String(MAXLEN = "");
Property service As %String(MAXLEN = "");
}

And this is the error:

Compilación iniciada en 04/20/2018 11:17:01 con calificadores 'k'
Compilando clase test.bp.TestBP
Compilando tabla test_bp.TestBP
Compilando tabla test_bp.TestBP_MasterPendingResponses
Compilando tabla test_bp.TestBP_MessagesReceived
Compilando tabla test_bp.TestBP_MessagesSent
Compilando tabla test_bp.TestBP__SynchronizedResponses
Compilando rutina test.bp.TestBP.1
Compilando rutina test.bp.TestBPMasterPendingResponses.1
Compilando rutina test.bp.TestBPMessagesReceived.1
Compilando rutina test.bp.TestBPMessagesSent.1
Compilando rutina test.bp.TestBPSynchronizedResponses.1
Compilando clase test.bp.TestBP.Context
Compilando clase test.bp.TestBP.Thread1
Compilando tabla test_bp_TestBP.Context
Compilando tabla test_bp_TestBP.Context__ResponseHandlers
Compilando tabla test_bp_TestBP.Thread1
Compilando tabla test_bp_TestBP.Thread1__ChildThreads
Compilando tabla test_bp_TestBP.Thread1__PendingResponses
Compilando tabla test_bp_TestBP.Thread1__SyncResponses
Compilando rutina test.bp.TestBP.Context.1
Compilando rutina test.bp.TestBP.ContextResponseHandlers.1
Compilando rutina test.bp.TestBP.Thread1.1
ERROR: test.bp.TestBP.Thread1.cls(S1+6) #1027: Error in SET command : 'status=1,callrequest.xmlTest.status_id="111"' : Offset:41 [zS1+4^test.bp.TestBP.Thread1.1]
 TEXT:  Set status=1,callrequest.xmlTest.status_id="111"

Compilando rutina test.bp.TestBP.Thread1ChildThreads.1
Compilando rutina test.bp.TestBP.Thread1PendingResponses.1
Compilando rutina test.bp.TestBP.Thread1SyncResponses.1
1 errores detectados al compilar.

We have tried to declare the property without underscore and use XMLNAME to set the name with underscore, but then, when we convert the object to JSON, it uses the real name, not which is defined in XMLNAME.

What can we do?

Thank you in advance.

Discussion (8)1
Log in or sign up to continue

Hi Laura,

I would declare the class property without the underscore as you have tried, given the way the generated code (in the class ending in ...Thread1.cls) interprets this and generates code.

Having said this - I'm interested in then knowing how you are going about generating the JSON string.   This is probably where you need to focus hour efforts and set a JSON element of "status_id".

How are you generating you JSON, and what version of Ensemble are you on ?  Answers to these questions will help others looking at your post to contribute a  solution.

Thanks - Steve

We are using 2016.2.1 (Build 803U).

This is the way we generate JSON:

TEST>set x = ##class(test.msg.struct.TestXML).%New()
 TEST>set x.statusId = "111"
 TEST>set x.service = "222"
 TEST>do ##class(Ens.Util.JSON).ObjectToJSONStream(x, .obj1, "aelotu")
 TEST>w obj1.Read()
{       "statusId":"111",       "service":"222"}
 

And this is the definition of the object:

Class test.msg.struct.TestXML Extends (%SerialObject,%XML.Adaptor)
{
Property statusId As %String(MAXLEN = "", XMLNAME = "status_id");
Property service As %String(MAXLEN = "");
}

I had already thought about it, but we have a lot of properties with underscore, we would have to change everyone manually.

We can do that, but I don't like it.

Maybe changing something in the method ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject() that if "XMLNAME" is defined, use that name instead property real name? But I don't know where to do that...

Following your code example, this might be the quickest workaround

 TEST>set x = ##class(test.msg.struct.TestXML).%New()
 TEST>set x.statusId = "111"
 TEST>set x.service = "222"
 TEST>do ##class(Ens.Util.JSON).ObjectToJSONStream(x, .obj1, "aelotu")
 TEST>write $REPLACE(obj1.Read(),"statusId","status_Id")
{       "status_Id":"111",       "service":"222"}

Not to pretty but useful.
 ​

Thanks to all of you.

I have extended the original class and tunned the method  %ObjectToJSON like this:

ClassMethod %ObjectToJSON(pObject As %RegisteredObject, ByRef pVisited, pLevel As %Integer = 0, pFormat As %String = "aceloqtw") As %Status
{
    Set tSC = $$$OK
    Try {
        If ((pObject="")||'$IsObject(pObject)||($D(pVisited(pObject)))) {
            // cycle
            Write:pFormat["a"||'pLevel "null"
            Quit
        }
        Set pVisited(pObject) = ""
        Set tClass = $classname(pObject)
        If (tClass = "%ZEN.proxyObject") {
            Set tSC = pObject.%ToJSON(pLevel,pFormat)
            Quit
        }
        Set tLF=$S(pFormat["w":$C(13,10), pFormat["n":$C(10), 1:"")
        If pFormat'=$TR(pFormat,"it123456789") { Set tN=+$ZStrip(pFormat,"<E'N"), $P(tTab,$S(pFormat["t":$C(9),1:" "),1+$S(tN:tN,pFormat["t":1,1:4))="" }
        Else { Set tTab="" }
        Set tIncludeWhitespace = (tLF_tTab'="")
        If (pObject.%Extends("%Collection.AbstractList")) {
            Set tList = pObject
            Set tCount = tList.Count()
            If (pFormat["l" || tCount) {
                Write "["
                For n = 1:1:tCount {
                    Set tValue = tList.GetAt(n)
                    Write:n>1 ","
                    If $IsObject(tValue) {
                        If (tValue.%IsA("%ZEN.proxyObject")) {
                            Set tSC = tValue.%ToJSON(pLevel+1,pFormat)
                            Quit:$$$ISERR(tSC)
                        } Else {
                            Set tSC = ..%ObjectToJSON(tValue,.pVisited, pLevel+1, pFormat)
                            Quit:$$$ISERR(tSC)
                        }
                    } Else {
                        Write $$$ZENJSONVALUE(tValue,pFormat)
                    }
                }
                Quit:$$$ISERR(tSC)
                If tIncludeWhitespace Set tIndent="", $P(tIndent,tTab,pLevel+1)="" Write tLF_tIndent
                Write "]"
            }
            Quit
        }
        ElseIf (pObject.%Extends("%Stream.Object")) {
            Write """"
            #; Initialize stream read length, if needed
            If '$data(tStreamMaxReadLen) Set tStreamMaxReadLen = ($$$MaxLocalLength\2)
            Do pObject.Rewind()
            While 'pObject.AtEnd {
                Write $$$ZENJSONESCAPE(pObject.Read(tStreamMaxReadLen),pFormat)
            }
            Write """"
            Quit
        }
        If pFormat["o" || 'pLevel {
            Set tPropCount = ""
            If (tIncludeWhitespace && pLevel) Set tIndent="", $P(tIndent,tTab,pLevel+1)="" Write $S(pFormat["b":tLF_tIndent,1:" ")
            Write "{"
        } Else {
            Set tPropCount = 0
        }
        If pFormat["c" {
            // add class name to model
            Do nextProp
            Write $$$ZENJSONPAIR("_class",tClass,pFormat)
            // add id for persistent objects
            If (pObject.%IsA("%Library.Persistent")) {
                Do nextProp
                Set tID = pObject.%Id()
                Write $$$ZENJSONPAIR("_id",tID,pFormat)
            }
        }
        #; Special treatment for top-level array: output no matter what
        If pObject.%Extends("%Collection.AbstractArray") {
            #; write out (eligible) array elements/properties
            If pObject.%Extends("%Collection.AbstractArrayOfObj") {
                #; object elements
                Set tKey=""  For { Set tValue = pObject.GetNext(.tKey)  Quit:""=tKey
                    If $IsObject(tValue) {
                        If tValue.%Extends("%Stream.Object")||tValue.%Extends("%IO.I.Stream") {
                            Do tValue.Rewind()
                            If (pFormat["e" || tValue.Size()) {
                                Do nextProp
                                Write $$$ZENJSONPROP(tKey,pFormat)_":"""
                                #; Initialize stream read length, if needed
                                If '$data(tStreamMaxReadLen) Set tStreamMaxReadLen = ($$$MaxLocalLength\2)
                                #; Rewind non-%IO streams if needed
                                If tValue.AtEnd && tValue.%Extends("%Stream.Object") Do tValue.Rewind()
                                While 'tValue.AtEnd {
                                    Write $$$ZENJSONESCAPE(tValue.Read(tStreamMaxReadLen),pFormat)
                                }
                                Write """"
                            }
                        } ElseIf pFormat["o" || ..hasObjContent(tValue,.pVisited,pFormat) {
                            Do nextProp
                            Write $$$ZENJSONPROP(tKey,pFormat)_":"
                            Set tSC = ..%ObjectToJSON(tValue,.pVisited, pLevel+1,pFormat)
                            Quit:$$$ISERR(tSC)
                        }
                    } ElseIf pFormat["a" {
                        Do nextProp
                        Write $$$ZENJSONPROP(tKey,pFormat)_":null"
                    }
                } ; end tKey object array loop
            } Else {
                #; scalar array elements
                Set tKey=""  For { Set tValue = pObject.GetNext(.tKey)  Quit:""=tKey
                    If (pFormat["e") || (tValue'="") {
                        Do nextProp
                        Write $$$ZENJSONPAIR(tKey,tValue,pFormat)
                    }
                } ; end tKey scalar array loop
            }
            If tPropCount'=0 {
                #; either we wrote at least one property or we wrote an empty '{' due to "o" mode or level zero
                If tIncludeWhitespace Set tIndent="", $P(tIndent,tTab,pLevel+1)="" Write tLF_tIndent
                Write "}"
            }
            Quit
        }
        #; else: main object is not a collection
        #; loop over properties using class meta-data
        Do ..getOrderedProps(tClass,.tProps)
        Set tSeq="" For { Set tSeq=$O(tProps(tSeq),1,tPropName)  Quit:""=tSeq
            Set tPrivate = +$$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPprivate)
            Continue:tPrivate||(tPropName["%")
            Set tType = $$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPtype)
            Set tClsType = $$$getClassType(tType)
            Set tClientType = $$$comClassKeyGet(tType,$$$cCLASSclientdatatype)
            Set tCollection = $$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPcollection)
            If (tClsType '= "datatype") {
                #; Check for the case where we have a property declared as a %ListOf**
                If ($classmethod(tType,"%IsA","%Collection.AbstractList")) {
                    Set tCollection = "list"
                    If ($classmethod(tType,"%IsA","%Collection.AbstractListOfDT")) {
                        #; Reset object information for %ListOfDataTypes
                        Set tClientType = "VARCHAR"
                        Set tDataType = ""
                    }
                }
            } Else {
                Set tDataType=$Case(tClientType, "BOOLEAN":"b", "INTEGER":"n","NUMERIC":"n","FLOAT":"n", "TIMESTAMP":"u", "DATE":"d", "TIME":"t", :"")
            }
            Set tMultiDim = 0
            If (tCollection="array") {
                Set tCardinality = $$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPcardinality)
                Set tInverse = $$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPinverse)
                If ((tCardinality'="")&&(tInverse'="")) {
                    // treat relationship as list
                    Set tCollection = "list"
                }
            } ElseIf (tCollection = "") {
                Set tMultiDim = +$$$comMemberKeyGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPmultidimensional)
            }
            Continue:tMultiDim
            Set tValue = $property(pObject,tPropName)
            #; If the value is "" or $c(0) and we are NOT including empty properties, skip if we are not a collection, object or stream
            If (((tValue = "") || (tValue = $c(0))) && (pFormat'["e") && (tCollection = "") && $Case(tClientType, "HANDLE": 0, "CHARACTERSTREAM": 0, "BINARYSTREAM": 0, :1)) {
                Continue
            }
            // Write the property if not inhibited
            If (tCollection="list") {
                // list collection
                If '$IsObject(tValue) {
                    Set tCount = 0
                } Else {
                    Set tList = tValue
                    Set tCount = tList.Count()
                }
                If (pFormat["l" || tCount) {
                    Do nextProp
                    if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                        Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":["
                    } else {
                        Write $$$ZENJSONPROP(tPropName,pFormat)_":["
                    }

                    For n = 1:1:tCount {
                        Set tValue = tList.GetAt(n)
                        Write:n>1 ","
                        If (tClientType = "HANDLE") {
                            #; object items
                            If $IsObject(tValue) {
                                Set tSC = ..%ObjectToJSON(tValue,.pVisited, pLevel+1, pFormat)
                                Quit:$$$ISERR(tSC)
                            } Else {
                                Write "null" ; not conditional because it has to hold the place in the list
                            }
                        } Else {
                            #; scalar list item ; converts $List to empty string!
                            Write $S((tDataType="b")&&(pFormat["e"):$S(tValue=1:"true",tValue=0:"false",1:"null")
                                , (tDataType="b"):$S(tValue:"true",1:"false")
                                , ((tDataType="n")||(pFormat["q"))&&$$$ZENJSISNUM(tValue):$$$ZENJSNUM(tValue)
                                , (tDataType="n")&&(tValue="")&&(pFormat["d"):"null"
                                , ($C(0)=tValue)||$ListValid(tValue):""""""
                                , 1:$$$ZENJSONSTR(tValue,pFormat))
                        }
                    }
                    Write "]"
                }
            }
            ElseIf (tCollection="array") {
                // array collection (object on client)
                If '$IsObject(tValue) {
                    Set tKey = ""
                } Else {
                    Set tArray = tValue
                    Set tKey = tArray.Next("")
                    If pFormat'["o" && (""'=tKey) {
                        #; look ahead to see if there is any content
                        Set tHasArrayContent=0, k=tKey  While (k '= "") { Set tValue = tArray.GetAt(k)
                            If (tClientType = "HANDLE") {
                                If $IsObject(tValue) {
                                    If ..hasObjContent(tValue,.pVisited,pFormat) Set tHasArrayContent=1  Quit
                                } ElseIf (pFormat["a") {
                                    Set tHasArrayContent=1  Quit
                                }
                            } Else {
                                If $S(tDataType="b":1
                                    , $C(0)=tValue||$ListValid(tValue):pFormat["e"
                                    //, "dtu"[tDataType:$S(pFormat["u":$$$ZENJSUSTR(..formatDateTime(tValue,tType,tDataType,pFormat)), 1:$$$ZENJSSTR(..formatDateTime(tValue,tType,tDataType,pFormat)))
                                    , 1:""'=tValue||(pFormat["e")) {
                                    Set tHasArrayContent=1  Quit
                                }
                            }
                            Set k = tArray.Next(k)
                        }
                    }
                }
                If (pFormat["o" || (""'=tKey && tHasArrayContent)) {
                    Do nextProp
                    if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                        Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_": {"
                    } else {
                        Write $$$ZENJSONPROP(tPropName,pFormat)_": {"
                    }

                    Set n = 0
                    While (tKey '= "") { Set tValue = tArray.GetAt(tKey)
                        If (tClientType = "HANDLE") {
                            #; object elements
                            If $IsObject(tValue) {
                                Set n = n+1
                                Write $S(n>1:",",1:"")_$$$ZENJSONPROP(tKey,pFormat)_":"
                                Set tSC = ..%ObjectToJSON(tValue,.pVisited, pLevel+1, pFormat)
                                Quit:$$$ISERR(tSC)
                            } ElseIf (pFormat["a") {
                                Set n = n+1
                                Write $S(n>1:",",1:"")_$$$ZENJSONPROP(tKey,pFormat)_":null"
                            }
                        } Else {
                            #; scalar array item ; converts $List to empty string!
                            Set tStr = $S((tDataType="b")&&(pFormat["e"):$S(tValue=1:"true",tValue=0:"false",1:"null")
                                , (tDataType="b"):$S(tValue:"true",1:"false")
                                , ((tDataType="n")||(pFormat["q"))&&$$$ZENJSISNUM(tValue):$$$ZENJSNUM(tValue)
                                , (tDataType="n")&&(tValue="")&&(pFormat["d"):"null"
                                , ($C(0)=tValue)||$ListValid(tValue):""""""
                                //, "dtu"[tDataType:$S(pFormat["u":$$$ZENJSUSTR(..formatDateTime(tValue,tType,tDataType,pFormat)), 1:$$$ZENJSSTR(..formatDateTime(tValue,tType,tDataType,pFormat)))
                                , 1:$$$ZENJSONSTR(tValue,pFormat))
                            If (pFormat["e") || (tStr'="""""") {
                                Set n = n+1
                                Write $S(n>1:",",1:"")_$$$ZENJSONPROP(tKey,pFormat)_":"_tStr
                            }
                        }
                        Set tKey = tArray.Next(tKey)
                    }
                    Write "}"
                }
            }
            ElseIf (tClientType = "HANDLE") {
                // object
                If $IsObject(tValue) {
                    If ..hasObjContent(tValue,.pVisited,pFormat) || (pFormat["o")  {
                        Do nextProp
                        if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                            Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":"
                        } else {
                            Write $$$ZENJSONPROP(tPropName,pFormat)_":"
                        }

                        Set tSC = ..%ObjectToJSON(tValue,.pVisited, pLevel+1, pFormat)
                        Quit:$$$ISERR(tSC)
                    }
                } ElseIf (pFormat["a") {
                    Do nextProp
                    if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                        Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":null"
                    } else {
                        Write $$$ZENJSONPROP(tPropName,pFormat)_":null"
                    }

                }
            }
            ElseIf (tClientType = "CHARACTERSTREAM") {
                If $IsObject(tValue) {
                    If tValue.Size || (pFormat["e") {
                        Do nextProp
                        if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                            Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":"""
                        } else {
                            Write $$$ZENJSONPROP(tPropName,pFormat)_":"""
                        }

                        If tValue.Size {
                            #; Initialize stream read length, if needed
                            If '$data(tStreamMaxReadLen) Set tStreamMaxReadLen = ($$$MaxLocalLength\2)
                            Do tValue.Rewind()
                            While 'tValue.AtEnd {
                                Write $$$ZENJSONESCAPE(tValue.Read(tStreamMaxReadLen),pFormat)
                            }
                        }
                        Write """"
                    }
                } ElseIf (pFormat["a") {
                    Do nextProp
                    if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                        Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":null"
                    } else {
                        Write $$$ZENJSONPROP(tPropName,pFormat)_":null"
                    }

                }
            }
            ElseIf (tClientType = "BINARYSTREAM") {
                Do nextProp
                if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                    Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":null"
                } else {
                    Write $$$ZENJSONPROP(tPropName,pFormat)_":null"
                }

            }
            Else {
                #; scalar item ; converts $List to empty string!
                Set tStr = $S((tDataType="b")&&(pFormat["e"):$S(tValue=1:"true",tValue=0:"false",1:"null")
                    , (tDataType="b"):$S(tValue:"true",1:"false")
                    , ((tDataType="n")||(pFormat["q"))&&$$$ZENJSISNUM(tValue):$$$ZENJSNUM(tValue)
                    , (tDataType="n")&&(tValue="")&&(pFormat["d"):"null"
                    , ($C(0)=tValue)||$ListValid(tValue):""""""
                    //, "dtu"[tDataType:$S(pFormat["u":$$$ZENJSUSTR(..formatDateTime(tValue,tType,tDataType,pFormat)), 1:$$$ZENJSSTR(..formatDateTime(tValue,tType,tDataType,pFormat)))
                    , 1:$$$ZENJSONSTR(tValue,pFormat))
                If (pFormat["e") || (tStr'="""""") {
                    Do nextProp
                    if ($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME") '= "") {
                        Write $$$ZENJSONPROP($$$comMemberArrayGet(tClass,$$$cCLASSproperty,tPropName,$$$cPROPparameter,"XMLNAME"),pFormat)_":"_tStr
                    } else {
                        Write $$$ZENJSONPROP(tPropName,pFormat)_":"_tStr
                    }

                }
            }
        } ; end properties loop
        Quit:$$$ISERR(tSC)
        If tPropCount'=0 {
            #; either we wrote at least one property or we wrote an empty '{' due to "o" mode or level zero
            If tIncludeWhitespace Set tIndent="", $P(tIndent,tTab,pLevel+1)="" Write tLF_tIndent
            Write "}"
        }
    }
    Catch ex {
        Set tSC = ex.AsStatus()
    }
    Quit tSC
nextProp
    If tPropCount=0 {
        If (tIncludeWhitespace && pLevel) Set tIndent="", $P(tIndent,tTab,pLevel+1)="" Write $S(pFormat["b":tLF_tIndent,1:" ")
        Write "{"
    } ElseIf tPropCount {
        Write ","
    } ; else tPropCount="" means we already did the starting '{' due to "o" mode
    Set tPropCount = tPropCount + 1
    If tIncludeWhitespace Set tIndent="", $P(tIndent,tTab,pLevel+2)="" Write tLF_tIndent
    Quit
}

And it works!

TEST>set x = ##class(test.msg.struct.TestXML).%New()
TEST>set x.statusId = "1111"
TEST>set x.service = "222"
TEST>do ##class(test.util.JsonProvider).%WriteJSONStreamFromObject(.obj1,.x,,,,"aelotuw")
TEST>w obj1.Read()
{
        "status_id":"1111",
        "service":"222"
}
TEST>

I didn't want to change system classes, so I thought to extend it and overrite what I need. For me is much clean this.