JSON to XML conversion
If a form POST needs to pass a lot of structured data, it might be advisable to prescribe an XML format in which the data should be passed. This enables validation of the data with an XSD and the readability makes debugging easier. However, building an XML document in javascript is neither elegant nor easy. It is much easier to just build the object structure and convert it to a JSON string, for instance using this helper library (2.3 KB when 'compressed' with a decent utility; smaller if you don't need the 'parse' method and strip it).
Allowing JSON to be posted required the receiver to convert the JSON to XML before validating it, which poses three problems
The second problem can be solved in much the same way: by prefixing the attributes-to-be, for instance with xml_attr_<elementname>. Another solution is keeping a (synchronised) list of the attributes appearing in the XSD, checking for their presence and converting them if required. Again, boh solutions have problems, similar to the solutions to the first problem.
The third problem can again be solved in two ways: either by requiring the JSON to be constructed in the correct order and keeping that order intact when converting the JSON to XML (for instance by using these classes, modified by changing the HashMap in JSONObject to a LinkedHashMap) or you could write code to magically impose the order required by the XSD on the resulting XML. The last solution poses a pretty daunting task, while the first solution is easier, provided the users receive clear feedback about ordering problems when testing the JSON they constructed.
After this analysis, our conclusion was that for our case, allowing the webdevelopers to post JSON is an acceptable solution, as we
Allowing JSON to be posted required the receiver to convert the JSON to XML before validating it, which poses three problems
- JSON doesn't have any namespaces
- JSON doesn't distinguish between elements and attributes, like XML does
- JSON doesn't really care about ordering
- Namespace the JSON elements in some way ( { ns1_element1: "bar" } )
- Magically determine which element is from which namespace
The second problem can be solved in much the same way: by prefixing the attributes-to-be, for instance with xml_attr_<elementname>. Another solution is keeping a (synchronised) list of the attributes appearing in the XSD, checking for their presence and converting them if required. Again, boh solutions have problems, similar to the solutions to the first problem.
The third problem can again be solved in two ways: either by requiring the JSON to be constructed in the correct order and keeping that order intact when converting the JSON to XML (for instance by using these classes, modified by changing the HashMap in JSONObject to a LinkedHashMap) or you could write code to magically impose the order required by the XSD on the resulting XML. The last solution poses a pretty daunting task, while the first solution is easier, provided the users receive clear feedback about ordering problems when testing the JSON they constructed.
After this analysis, our conclusion was that for our case, allowing the webdevelopers to post JSON is an acceptable solution, as we
- All our elements are in one namespace,
- The webdevelopers see no problem in making sure the JSON is ordered correctly
- An attribute prefix does not really limit the possibilities, as long as it is documented, the webdevelopers are kept aware of it and clear feedback points out where they forgot to mark an attribute
A namespace gotcha in XSL transformations
Say you have an xml document that conforms to the schema it references
You use the default namespace for the namespace from which you will reference the most element, to keep the document as short and readable as possible.
Now you want to transform this bit of XML using an XSLT:
and you expect the output to read
Unfortunately, this won't work, because of this tiny fact from section 2.4 of the XSLT specification:
I'm still not sure why this is the case, but I do know it took me quite a while to figure out...
NB. I know this schema.xsd schemalocation reference will only work for a local file and even then only in some cases. Replace schema.xsd by
http://xml.mydomain.nl/meaningful-path/1.0/schema.xsd before nitpicking about syntax
XML:
1
2
3
4
5
6
7
8
| <?xml version="1.0" encoding="UTF-8"?> <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml.mydomain.nl/meaningful-path/1.0 schema.xsd" xmlns="http://xml.mydomain.nl/meaningful-path/1.0"> <foo>Foo!</foo> </root> |
You use the default namespace for the namespace from which you will reference the most element, to keep the document as short and readable as possible.
Now you want to transform this bit of XML using an XSLT:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://xml.mydomain.nl/meaningful-path/1.0" version="1.0"> <xsl:output method="xml" indent="yes" encoding="UTF-8" /> <xsl:strip-space elements="*" /> <xsl:template match="/"> <xsl:apply-templates select="//root"/> </xsl:template> <xsl:template match="root"> <bar><xsl:value-of select="foo"</bar> </xsl:template> </xsl:stylesheet> |
and you expect the output to read
XML:
1
2
3
4
5
6
| <?xml version="1.0" encoding="UTF-8"?> <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml.mydomain.nl/meaningful-path/1.0 schema.xsd" xmlns="http://xml.mydomain.nl/meaningful-path/1.0"> <bar>Foo!</bar> </root> |
Unfortunately, this won't work, because of this tiny fact from section 2.4 of the XSLT specification:
As a result, only this will work:The default namespace is not used for unprefixed names.
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://xml.mydomain.nl/meaningful-path/1.0" version="1.0"> <xsl:output method="xml" indent="yes" encoding="UTF-8" /> <xsl:strip-space elements="*" /> <xsl:template match="/"> <xsl:apply-templates select="//ns1:root"/> </xsl:template> <xsl:template match="ns1:root"> <bar><xsl:value-of select="ns1:foo"</bar> </xsl:template> </xsl:stylesheet> |
I'm still not sure why this is the case, but I do know it took me quite a while to figure out...
NB. I know this schema.xsd schemalocation reference will only work for a local file and even then only in some cases. Replace schema.xsd by
http://xml.mydomain.nl/meaningful-path/1.0/schema.xsd before nitpicking about syntax
