imperialcode
imperialcode

Reputation: 11

XSLT: Json to given xml format

Problem: I have a given JSON output and need to transform this into a xml with a given format to use it for another input.

The JSON-document: report.json

 {
  "Diagnostic Cycle" : "2019-02-13T08:19:44ZZ",
    "01 Tester" : {
      "01 Name"              : "IPTester",
      "02 Operating System"  : "Linux",
      "03 Node Name"         : "tester15",
      "04 Release Level"     : "5.4.7",
      "06 Machine"           : "i686",
      "07 Domain Name"       : "(none)" 
    } 
,
    "02 Device Name" : 
{

      "SampleECU":
      {
      "01 Diagnostic"        : "OK",
      "02 CAN Id"            : "(none)",
      "02 DoIP Id"           : "00FFh 124Ah 85B1h",
      "03 VIN original"      : "BZ7282399843",
      "04 VIN current"       : "ERROR 11",
      "05 HW Part No"        : "887895414",
      "06 DTC Status 01"     : 1,
      "10 Hardware Year"     : 2020,
      "11 Hardware Week"     : 08,
      "12 Hardware Patch"    : 0,
      "20 Software Year"     : 2020,
      "21 Software Week"     : 08,
      "22 Software Patch"    : 0,
      "30 Bootware"          : "ERROR 11"
    }
}

How the XML-output should look:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="./My_Stylesheet.xsl"?>
<VehicleReport xsi:schemaLocation="http://www.w3.org/xsd/vdx30 VDX.3.2.1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VDXVersion="3.2.1" xmlns="http://www.w3.org/xsd/vdx30">
    <ServiceTool>
        <Name>IPTester</Name>
        <Version>5.4.7</Version>
        <UserID>tester15</UserID>
        <ExecutionTime>2019-02-13T08:19:44ZZ</ExecutionTime>
    </ServiceTool>
    <VehicleInformation>
        <IdentificationNumberValue>BZ7282399843</IdentificationNumberValue>
    </VehicleInformation>
    <ComponentList>
        <Component>
            <ECUShortName>SampleECU</ECUShortName>
            <DiagnosticInfo>
                <DiagnosticInfoValue>1</DiagnosticInfoValue>
            </DiagnosticInfo>
            <SWHWInformation>
                <Software>
                    <Version>
                        <VersionValue>20/08/00</VersionValue>
                    </Version>
                </Software>
                <Hardware>
                    <PartNumber>
                        <PartNumberValue>887895414</PartNumberValue>
                    </PartNumber>
                    <Version>
                        <VersionValue>20/08/00</VersionValue>
                    </Version>
                </Hardware>
            </SWHWInformation>
        </Component>
      </ComponentList>
</VehicleReport>

I heard I should use the Saxon XSLT Processor for a XSLT-transformation, but I dont know how (no XSLT experience).

The two ways I can think of:

  1. First use json-to-xml() then transform the xml with XSLT
  2. Populate the XML directly with JSON-values (preferred because simple)

But I dont know how to do both - a tutorial for XSLT would be appreciated.

Upvotes: 1

Views: 425

Answers (2)

Michael Kay
Michael Kay

Reputation: 163322

Both approaches are possible. Martin has given you an indication of the first approach (use json-doc() or parse-json() to convert the JSON to maps and arrays, then populate the XML document by selecting into those maps and arrays. The other approach is to convert to "generic" XML and then transform the generic XML using template rules.

Transformations (whether XML-to-XML or JSON-to-XML) are generally either input-driven or output-driven. In an example like yours where the structure of the output bears little relation to the structure of the input, you need to be output-driven: that is, the stylesheet will take the form "Generate XXX by fetching /a/b/c from the input, then generate YYY by fetching /p/q/r from the input". That's a strong indicator to use the style of solution Martin has put forward.

The other style, where you convert JSON to generic XML and then apply templates to the generic XML to produce your specific XML, is probably more suited to "input-driven" transformations, which take the typical form "look to see what comes next in the input, and depending on what you find, generate X or Y or Z in the output".

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167516

To give you one example on how to operate with XPath 3.1 on JSON values which are represented in the XPath 3.1 data model as maps and arrays which are functions you can call with a property name (for maps/objects) or the index (for arrays) as the argument here is a simple XQuery 3.1 code that directly populates your XML (I have only put in the XPath expressions for a few values, you should be able to fill in the rest):

<VehicleReport xsi:schemaLocation="http://www.w3.org/xsd/vdx30 VDX.3.2.1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VDXVersion="3.2.1" xmlns="http://www.w3.org/xsd/vdx30">
    <ServiceTool>
        <Name>{.("01 Tester")("01 Name")}</Name>
        <Version>{.("01 Tester")("04 Release Level")}</Version>
        <UserID>{.("01 Tester")("03 Node Name")}</UserID>
        <ExecutionTime>{.("Diagnostic Cycle")}</ExecutionTime>
    </ServiceTool>
    <VehicleInformation>
        <IdentificationNumberValue>{.("02 Device Name")("SampleECU")("03 VIN original")}</IdentificationNumberValue>
    </VehicleInformation>
    <ComponentList>
        <Component>
            <ECUShortName>SampleECU</ECUShortName>
            <DiagnosticInfo>
                <DiagnosticInfoValue>1</DiagnosticInfoValue>
            </DiagnosticInfo>
            <SWHWInformation>
                <Software>
                    <Version>
                        <VersionValue>20/08/00</VersionValue>
                    </Version>
                </Software>
                <Hardware>
                    <PartNumber>
                        <PartNumberValue>887895414</PartNumberValue>
                    </PartNumber>
                    <Version>
                        <VersionValue>20/08/00</VersionValue>
                    </Version>
                </Hardware>
            </SWHWInformation>
        </Component>
      </ComponentList>
</VehicleReport>

This assumes the JSON is valid JSON like

{
  "Diagnostic Cycle" : "2019-02-13T08:19:44ZZ",
    "01 Tester" : {
      "01 Name"              : "IPTester",
      "02 Operating System"  : "Linux",
      "03 Node Name"         : "tester15",
      "04 Release Level"     : "5.4.7",
      "06 Machine"           : "i686",
      "07 Domain Name"       : "(none)" 
    } 
,
    "02 Device Name" : 
{

      "SampleECU":
      {
      "01 Diagnostic"        : "OK",
      "02 CAN Id"            : "(none)",
      "02 DoIP Id"           : "00FFh 124Ah 85B1h",
      "03 VIN original"      : "BZ7282399843",
      "04 VIN current"       : "ERROR 11",
      "05 HW Part No"        : "887895414",
      "06 DTC Status 01"     : 1,
      "10 Hardware Year"     : 2020,
      "11 Hardware Week"     : 8,
      "12 Hardware Patch"    : 0,
      "20 Software Year"     : 2020,
      "21 Software Week"     : 8,
      "22 Software Patch"    : 0,
      "30 Bootware"          : "ERROR 11"
    }
}
}

and is provided in XQuery as the context item e.g. with declare context item := json-doc('file.json');.

Saxon 9 supports XQuery 3.1 as well as XSLT 3, therefore, as long as you have a simple XML document you want to populate, it seems a bit easier to use XQuery than XSLT as there is no boilerplate for templates.

Online example is at https://xqueryfiddle.liberty-development.net/bFDbxkV.

So the main issues to learn is to understand that a JSON object { "foo" : 1, "bar": "value" } is represented as a map which also is a function you can call with a property name (e.g. .("foo") or .("bar")) to select a property. For your JSON property names containing spaces this seems the way to go, for the simple example I have shown the shortfix ?foo or ?bar would suffice.

Upvotes: 0

Related Questions