Vivek Kalyanarangan
Vivek Kalyanarangan

Reputation: 9081

XML to JSON Conversion Issue

I have an xml schema -

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns="http://www.tibco.com/schemas/RegTestingStub/SharedResources/SchemaDefinitions/CommonXSD/Schema.xsd2"
     targetNamespace="http://www.tibco.com/schemas/RegTestingStub/SharedResources/SchemaDefinitions/CommonXSD/Schema.xsd2"
     elementFormDefault="qualified"
     attributeFormDefault="unqualified">
    <xs:element name="AccumulateResponse" type="AccumulateResponse"/>
    <xs:complexType name="AccumulateResponse">
        <xs:sequence>
            <xs:element name="TestCase" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="Transactionid" type="xs:string"/>
                        <xs:element name="TransactionType" type="xs:string"/>
                        <xs:element name="Status" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

I am doing this conversion i the following steps - 1) Generate Java Classes from XML Schema using XJC 2) Compile the classes using Java Compiler API 3) unmarshalling the class instance by JAXB

But they are all heavy IO operations. Is there a way to do it in memory?

Upvotes: 2

Views: 3253

Answers (3)

Vivek Kalyanarangan
Vivek Kalyanarangan

Reputation: 9081

Finally cracked this up -

For xml to json conversion -

  1. Parse the xml schema (JAXB) and create objects that can be stored in memory

  2. validate the xml with respect to the schema object created

  3. Create instance object of the xml string w.r.t the schema classes

  4. Use that instance to come up with the json string.

This way the json string will have data types as the schema dictates. E.g. - a particular element defined repeating in the schema will appear as a json array even if its occurrence is 1 in the xml (it wont be converted to json object). element with value 123 will be interpreted as string if explicitly defined in the schema and not implicitly converted to integer.

Contact me if anyone wants the code for it.

Upvotes: 1

lexicore
lexicore

Reputation: 43651

Disclaimer: I am the author of Jsonix, a schema-driven JavaScript library for XML<->JS conversion.

Jsonix seems to do exactly what you want:

  • you can generate a (JS) mapping from your schema
  • use this mapping to unmarshal your XML into JSON
  • marshal your JSON into XML

And just as you want it, Jsonix is type-aware and schema-aware.

Type-aware means you'll get in JSON a string where you have xs:string and a number where you have xs:decimal etc. Jsonix supports almost all built-in XML Schema types (as well as your own simple types derived from built-in types).

Schema-aware means that your JSON structure will be strictly based on the structure of your XML Schema. You'll get array where you have a repeatable element and so on.

Works in browsers as well as in Node.js, compatible with AMD as well as CJS environments.


Ok, enough words, let the code speak.

We take the following schema (I've named the file ar.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns="urn:test"
     targetNamespace="urn:test"
     elementFormDefault="qualified"
     attributeFormDefault="unqualified">
    <xs:element name="AccumulateResponse" type="AccumulateResponse"/>
    <xs:complexType name="AccumulateResponse">
        <xs:sequence>
            <xs:element name="TestCase" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="TransactionId" type="xs:string"/>
                        <xs:element name="TransactionType" type="xs:string"/>
                        <xs:element name="Status" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

(I've just modified one element name and the namespace.)

We first generate the mapping file using Jsonix schema compiler:

java -jar jsonix-schema-compiler-full.jar ar.xsd -p AR

What we get from this is a mapping file called AR.js which defines XML-JSON mappings. This is how the mapping definition looks like:

  {
    name: 'AR',
    defaultElementNamespaceURI: 'urn:test',
    typeInfos: [{
        localName: 'AccumulateResponse',
        propertyInfos: [{
            name: 'testCase',
            collection: true,
            elementName: 'TestCase',
            typeInfo: '.AccumulateResponse.TestCase'
          }]
      }, {
        localName: 'AccumulateResponse.TestCase',
        propertyInfos: [{
            name: 'transactionId',
            elementName: 'TransactionId'
          }, {
            name: 'transactionType',
            elementName: 'TransactionType'
          }, {
            name: 'status',
            elementName: 'Status'
          }]
      }],
    elementInfos: [{
        elementName: 'AccumulateResponse',
        typeInfo: '.AccumulateResponse'
      }]
  }

(There's also a compact mode where you get really short names like en instead of elementName.)

Next here's an XML sample to test (sample01.xml):

<?xml version="1.0" encoding="utf-8"?>
<AccumulateResponse xmlns="urn:test">
  <TestCase>
    <Transactionid>1234</Transactionid>
    <TransactionType>5678</TransactionType>
    <Status>Status01</Status>
  </TestCase>
  <TestCase>
    <Transactionid>true</Transactionid>
    <TransactionType>false</TransactionType>
    <Status>Status02</Status>
  </TestCase>
</AccumulateResponse>

Here's an unmarshalling test case (ar-tests.js):

    var Jsonix = require('jsonix').Jsonix;
    var AR = require('../AR').AR;

    // Create Jsonix context
    var context = new Jsonix.Context([ AR ]);

    // Create unmarshaller
    var unmarshaller = context.createUnmarshaller();

    // Unmarshal the XML file
    unmarshaller.unmarshalFile( 'tests/sample01.xml',
        function(element) {
            console.log(element.value);
            test.equal('Status01', element.value.testCase[0].status);
            test.done();
    });

And this is what you get in the console:

{ TYPE_NAME: 'AR.AccumulateResponse',
  testCase:
   [ { TYPE_NAME: 'AR.AccumulateResponse.TestCase',
       transactionType: '5678',
       status: 'Status01' },
     { TYPE_NAME: 'AR.AccumulateResponse.TestCase',
       transactionType: 'false',
       status: 'Status02' } ] }

Marshalling works the same way.

The full code of this example is available here. It is implemented with Node.js/nodeunit but Jsonix also works in the browser. (You don't have the unarshalFile feature but you can do unmarshalString or unmarshalDocument etc.)

I will post an example of JSFiddle later on.

Few links:

Upvotes: 1

Piotr
Piotr

Reputation: 649

You can use http://www.xsd2xml.com/ for generate sample xml from your xsd and after that you can use site: http://www.utilities-online.info/xmltojson/ to convert xml to json.

For your xsd, I got sample xml:

<?xml version="1.0" encoding="utf-8"?>
<AccumulateResponse>
  <TestCase>
    <Transactionid>str1234</Transactionid>
    <TransactionType>str1234</TransactionType>
    <Status>str1234</Status>
  </TestCase>
  <TestCase>
    <Transactionid>str5678</Transactionid>
    <TransactionType>str5678</TransactionType>
    <Status>str5678</Status>
  </TestCase>
</AccumulateResponse>

And using the second site I got:

{
"AccumulateResponse": {
  "TestCase": [
   {
    "Transactionid": "str1234",
    "TransactionType": "str1234",
    "Status": "str1234"
   },
   {
    "Transactionid": "str5678",
    "TransactionType": "str5678",
    "Status": "str5678"
   }
  ]
 }
}

Upvotes: 1

Related Questions