Question
· May 16, 2018

XML string to Serial Object

Hello.

I want to know how can I convert XML String intro a %SerialObject.

This is an example:

<Envelope>
    <Body>
        <RESULT>
            <SUCCESS>TRUE</SUCCESS>
            <LIST>
                <ID>11111</ID>
                <NAME>one</NAME>
            </LIST>
            <LIST>
                <ID>22222</ID>
                <NAME>two</NAME>
            </LIST>
        </RESULT>
    </Body>
</Envelope>

And this are the %SerialObject classes I have created:

Class test.Envelope Extends (%SerialObject,%XML.Adaptor)
{
Property Body As test.Body;
}
Class test.Body Extends (%SerialObject,%XML.Adaptor)
{
Property RESULT As test.Result;
}
Class test.Result Extends (%SerialObject,%XML.Adaptor)
{
Property SUCCESS As %String(MAXLEN = "");
Property LIST As list Of test.List;
}
Class test.List Extends (%SerialObject,%XML.Adaptor)
{
Property ID As %String(MAXLEN = "");
Property NAME As %String(MAXLEN = "");
}

I have tested with a function like this:

set reader = ##class(%XML.Reader).%New()
set sc = reader.OpenStream(xmlStream)
do reader.Rewind()
do reader.CorrelateRoot("test.Envelope")
while (reader.Next(.tMessage,.sc)) {
    set OUT = tMessage
}

But it only works if property "LIST" is a single object and not a list of objects. When I try with the list, I get this error:

ERROR #6237: Unexpected tag in XML input: LIST (ending at line 6 character 5).

How can I convert a XML String into a SerialObject?

Thank you in advance.

Discussion (9)2
Log in or sign up to continue

Hi Laura,

I find it simpler to write an XSD (even if you don't have one) and then use the XML code generator wizard.

A good place to start is by using an online tool that will auto generate an XSD from XML for you, such at the one you can find here...

https://www.liquid-technologies.com/online-xml-to-xsd-converter

Sometimes you will need to massage the XML a little first, for instance add a second instance of an element when you only have one in your example and you know there will be many.

So you would end up with an XSD that looks like this...

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Envelope">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Body">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="RESULT">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="SUCCESS" type="xs:string" />
                    <xs:element maxOccurs="unbounded" name="LIST">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="ID" type="xs:unsignedShort" />
                          <xs:element name="NAME" type="xs:string" />
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Save this to a file and from studio select "Tools" from the main menu, then "add-ins" and then "XML Schema Wizard" which might be visible or you might need to click "add-ins" again to bring it into focus.

Select the file, click next, de-select the "Create Persistent Classes" option and type in a package name, click next and select "Serial" for each of the classes, click next and the classes will be generated. You might need to go and compile them before they can be used. The generated code looks like this...

Class Spuds.Envelope Extends (%SerialObject, %XML.Adaptor) [ ProcedureBlock ]
{

Parameter XMLNAME = "Envelope";

Parameter XMLSEQUENCE = 1;

Property Body As Spuds.RESULT(XMLNAME = "Body", XMLPROJECTION = "WRAPPED") [ Required ];

}
Class Spuds.RESULT Extends (%SerialObject, %XML.Adaptor) [ ProcedureBlock ]
{

Parameter XMLNAME = "RESULT";

Parameter XMLSEQUENCE = 1;

Property SUCCESS As %String(MAXLEN = "", XMLNAME = "SUCCESS") [ Required ];

Property LIST As list Of Spuds.LIST(XMLNAME = "LIST", XMLPROJECTION = "ELEMENT") [ Required ];

}
Class Spuds.LIST Extends (%SerialObject, %XML.Adaptor) [ ProcedureBlock ]
{

Parameter XMLNAME = "LIST";

Parameter XMLSEQUENCE = 1;

Property ID As %xsd.unsignedShort(XMLNAME = "ID") [ Required ];

Property NAME As %String(MAXLEN = "", XMLNAME = "NAME") [ Required ];

}

And here is the solution in action...

TEST>s xml="<Envelope><Body><RESULT><SUCCESS>TRUE</SUCCESS><LIST><ID>11111</ID><NAME>one</NAME></LIST><LIST><ID>22222</ID><NAME>two</NAME></LIST></RESULT></Body></Envelope>"
 
TEST>set reader=##class(%XML.Reader).%New()                                      

TEST>set sc=reader.OpenString(xml)                                               

TEST>do reader.Correlate("Envelope","Spuds.Envelope")                            

TEST>do reader.Next(.envelope,.sc)                                               
 
TEST>w envelope.Body.SUCCESS
TRUE

TEST>w envelope.Body.LIST.GetAt(1).ID
11111

TEST>w envelope.Body.LIST.GetAt(1).NAME
one

Hi there Sean,

Do you know whether is there anything to perform the opposite (convert an object into a xml string)?

E.g: 

Class BaseClass.Harvest Extends (%SerialObject, %XML.Adaptor) [ ProcedureBlock ]
{
Parameter ELEMENTQUALIFIED = 1;
...
Property Number As %String(MAXLEN = "", XMLNAME = "Number");
Property PatientNumber As %String(MAXLEN = "", XMLNAME = "PatientNumber");
...

Outcome would be:

<xml>
<Harvest>

   <Number></Number>
   <PatientNumber></PatientNumber>
...

Many thanks