Reputation: 9081
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
Reputation: 9081
Finally cracked this up -
For xml to json conversion -
Parse the xml schema (JAXB) and create objects that can be stored in memory
validate the xml with respect to the schema object created
Create instance object of the xml string w.r.t the schema classes
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
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:
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
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