1

I'm doing research to solve a transformation problem that is fundamentally based entirely in JSON, and while imperative code-based solutions are possible, there is appeal in using an established technology like XSLT and leveraging its many features rather than trying to in-house everything from scratch. I'm fairly inexperienced with XSLT but I would love for this effort to provide a reason to learn it.

I've read that JSON is included in XSLT 3.0 and XPath 3.1 specs, and that Saxon-JS supports these, and furthermore that Saxon-JS has a node.js version. This has me very interested, but I haven't found much in the way of tutorials that provide instruction about how to transform JSON into other JSON; instead the focus appears to be on transforming JSON into XML.

This question is kind of broad and so I'd be happy to know just whether this is possible, and maybe where to look to start learning, but stackoverflow likes examples. With that in mind, what would a stylesheet look like that converts the first of these JSON documents to the second?

Source:

{
    "contents" : [
        {
            "id": 1,
            "color": "blue",
            "hexcode": "#0000FF"
        },
        {
            "id": 2,
            "color": "green",
            "hexcode": "#00FF00"
        },
        {
            "id": 3,
            "color": "red",
            "hexcode": "#FF0000"
        }
    ]
}

Output:

[
    {
        "id": "contents1",
        "color": {
            "name": "blue",
            "hexcode": "#0000FF"
        }
    },
    {
        "id": "contents2",
        "color": {
            "name": "green",
            "hexcode": "#00FF00"
        }
    },
    {
        "id": "contents3",
        "color": {
            "name": "red",
            "hexcode": "#FF0000"
        }
    }
]
4

2 回答 2

2

If you use

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all">
  
  <xsl:output method="json" indent="yes"/>
  
  <xsl:template match=".[. instance of map(*)]">
    <xsl:sequence
      select="array{ ?contents?*!map { 'id' : 'contents' || ?id, 'color' : map { 'name' : ?color, 'hexcode' : ?hexcode } } }"/>
  </xsl:template>
  
</xsl:stylesheet>

e.g. at the Saxon-JS fiddle then the JSON is parsed into an XPath 3.1 map and an XPath 3.1 array is constructed that is serialized back as JSON.

For the Node.js xslt3 command line tool you can use -json:my-input.json I think instead of the s:my-source.xml you would use to transform XML. I think that -json option is only in Saxon-JS 2.3 so make sure you use the latest version.

For a more general treatment and a debate of advantages and shortcoming of working only on the XDM representation of maps/arrays of JSON instead of using the XML representation you might get from json-to-xml see https://www.saxonica.com/papers/xmlprague-2016mhk.pdf, https://dev.saxonica.com/blog/mike/2017/11/transforming-json.html, https://www.youtube.com/watch?v=hGehtNUrg60.

For the syntax to query XPath 3.1 maps and arrays see https://www.altova.com/training/xpath3/xpath-31 or the XPath 3.1 spec or any other tutorial on XPath 3.1 arrays/maps.

于 2022-02-15T09:48:48.673 回答
2

Yes, it's possible, as Martin shows. However, XSLT still has a little way to go before JSON-to-JSON transforms work as smoothly as XML-to-XML. The main limitations in my experience are:

(a) it isn't as easy to write match patterns for xsl:template that match "nodes" in a JSON tree

(b) because there's no parent or ancestor axis for JSON, the processing of a particular "node" in the JSON tree has no access to the context in which it appears, which means it's necessary to pass that context down using template parameters (tunnel parameters come in very handy for this.)

In your example, the input data is completely flat, so these problems don't show up.

于 2022-02-15T13:57:54.567 回答