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 = "");
}
{
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.
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.
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.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:
{
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...
WOW !
Following your code example, this might be the quickest workaround
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")
Not to pretty but useful.
All properties names are written in:
To get current property XMLNAME parameter call this:
I had that feeling that it might be more.
And took a look to ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject() as it is visible.
It's nothing I would like nor to touch nor ever to maintain over future releases in parallel to ISC.
Thanks to all of you.
I have extended the original class and tunned the method %ObjectToJSON like this:
{
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'="")
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
}
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)
}
}
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
Do ..getOrderedProps(tClass,.tProps)
Set tSeq="" For { Set tSeq=$O(tProps(tSeq),1,tPropName) Quit:""=tSeq
Continue:tPrivate||(tPropName["%")
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
#; 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))
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)
#; 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
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.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.
Social networks
InterSystems resources
Log in or sign up
Log in or create a new account to continue
Log in or sign up
Log in or create a new account to continue
Log in or sign up
Log in or create a new account to continue