0

Following my question regarding (Convert JSON to XML using XSLT 3.0 - escape ampersand in element key) I ended up with following XML map.

<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
   <string key="@odata.context">https://swdev.api.crm4.dynamics.com/api/data/v9.0/$metadata#msdyn_bookingjournals(msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode)</string>
   <array key="value">
      <map>
         <string key="@odata.etag">W/"3935842"</string>
         <string key="msdyn_journaltype@OData.Community.Display.V1.FormattedValue">Travel</string>
         <number key="msdyn_journaltype">690970002</number>
         <string key="msdyn_name">856900132906 - Wartung  - Wartung - 101 Innener Verkehrsring/Düsseldorferstr </string>
         <string key="msdyn_starttime@OData.Community.Display.V1.FormattedValue">13.11.2018 13:11</string>
         <string key="msdyn_starttime">2018-11-13T13:11:58Z</string>
         <string key="msdyn_endtime@OData.Community.Display.V1.FormattedValue">13.11.2018 13:15</string>
         <string key="msdyn_endtime">2018-11-13T13:15:58Z</string>
         <string key="msdyn_duration@OData.Community.Display.V1.FormattedValue">4</string>
         <number key="msdyn_duration">4</number>
         <string key="foc_sapstatisticalkeyfigurecod"/>
         <string key="statecode@OData.Community.Display.V1.FormattedValue">Active</string>
         <number key="statecode">0</number>
         <string key="msdyn_bookingjournalid">cb6d62ee-49e7-e811-a958-000d3a29fea4</string>
      </map>
      <map>
         <string key="@odata.etag">W/"3935846"</string>
         <string key="msdyn_journaltype@OData.Community.Display.V1.FormattedValue">Working Hours</string>
         <number key="msdyn_journaltype">690970000</number>
         <string key="msdyn_name">856900132906 - Wartung  - Wartung - 101 Innener Verkehrsring/Düsseldorferstr </string>
         <string key="msdyn_starttime@OData.Community.Display.V1.FormattedValue">13.11.2018 13:15</string>
         <string key="msdyn_starttime">2018-11-13T13:15:58Z</string>
         <string key="msdyn_endtime@OData.Community.Display.V1.FormattedValue">13.11.2018 13:20</string>
         <string key="msdyn_endtime">2018-11-13T13:20:57Z</string>
         <string key="msdyn_duration@OData.Community.Display.V1.FormattedValue">4</string>
         <number key="msdyn_duration">4</number>
         <string key="foc_sapstatisticalkeyfigurecod"/>
         <string key="statecode@OData.Community.Display.V1.FormattedValue">Active</string>
         <number key="statecode">0</number>
         <string key="msdyn_bookingjournalid">fe6d62ee-49e7-e811-a958-000d3a29fea4</string>
      </map>
   </array>
   <string key="@odata.nextLink">https://xx.api.crm4.dynamics.com/api/data/v9.0/bookableresourcebookings(b0fe5834-45e7-e811-a958-000d3a29fb7a)/msdyn_bookableresourcebooking_msdyn_bookingjournal_Booking?$select=msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode&amp;$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%2520parentEntityId%253d%2522b0fe5834-45e7-e811-a958-000d3a29fb7a%2522%2520parentAttributeName%253d%2522msdyn_booking%2522%2520parentEntityObjectTypeCode%253d%25221145%2522%253e%253cmsdyn_bookingjournalid%2520last%253d%2522%257bFE6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520first%253d%2522%257bCB6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E</string>
</map>

As described in another users question (Convert JSON to XML using XSLT 3.0 functions), I wanted to convert the string / number elements to the desired XML format. Unfortunately, the '@' sign in the key names in causing trouble :

Unable to generate the XML document using the provided XML/XSL input.
Invalid element name. Invalid QName {@odata.context}

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs math" version="3.0">
    <xsl:output indent="yes" />
<xsl:template match="*[@key]" xpath-default-namespace="http://www.w3.org/2005/xpath-functions">
    <xsl:element name="{@key}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>

How can I remove all '@' signs in the element names of the XML map (preferably for nested element names too) ?

Edit : this was the original JSON input

{
    "@odata.context": "https:\/\/xxx.api.crm4.dynamics.com\/api\/data\/v9.0\/$metadata#msdyn_bookingjournals(msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode)",
    "value": [
    {
        "@odata.etag": "W\/\"3935842\"",
        "msdyn_journaltype@OData.Community.Display.V1.FormattedValue": "Travel",
        "msdyn_journaltype": 690970002,
        "msdyn_name": "Wartung  - Wartung - 101 Innener Verkehrsring\/D\u00fcsseldorferstr ",
        "msdyn_starttime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:11",
        "msdyn_starttime": "2018-11-13T13:11:58Z",
        "msdyn_endtime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:15",
        "msdyn_endtime": "2018-11-13T13:15:58Z",
        "msdyn_duration@OData.Community.Display.V1.FormattedValue": "4",
        "msdyn_duration": 4,
        "foc_sapstatisticalkeyfigurecod": "",
        "statecode@OData.Community.Display.V1.FormattedValue": "Active",
        "statecode": 0,
        "msdyn_bookingjournalid": "cb6d62ee-49e7-e811-a958-000d3a29fea4"
    },
    {
        "@odata.etag": "W\/\"3935846\"",
        "msdyn_journaltype@OData.Community.Display.V1.FormattedValue": "Working Hours",
        "msdyn_journaltype": 690970000,
        "msdyn_name": "Wartung  - Wartung - 101 Innener Verkehrsring\/D\u00fcsseldorferstr ",
        "msdyn_starttime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:15",
        "msdyn_starttime": "2018-11-13T13:15:58Z",
        "msdyn_endtime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:20",
        "msdyn_endtime": "2018-11-13T13:20:57Z",
        "msdyn_duration@OData.Community.Display.V1.FormattedValue": "4",
        "msdyn_duration": 4,
        "foc_sapstatisticalkeyfigurecod": "",
        "statecode@OData.Community.Display.V1.FormattedValue": "Active",
        "statecode": 0,
        "msdyn_bookingjournalid": "fe6d62ee-49e7-e811-a958-000d3a29fea4"
    }
    ],
    "@odata.nextLink": "https:\/\/xxx.api.crm4.dynamics.com\/api\/data\/v9.0\/bookableresourcebookings(b0fe5834-45e7-e811-a958-000d3a29fb7a)\/msdyn_bookableresourcebooking_msdyn_bookingjournal_Booking?$select=msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%2520parentEntityId%253d%2522b0fe5834-45e7-e811-a958-000d3a29fb7a%2522%2520parentAttributeName%253d%2522msdyn_booking%2522%2520parentEntityObjectTypeCode%253d%25221145%2522%253e%253cmsdyn_bookingjournalid%2520last%253d%2522%257bFE6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520first%253d%2522%257bCB6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20\/%3E"
}

To which cdata tags were added as the '@odata.nextLink' element contains urls that can have & in them.

<data>
    <![CDATA[
        {
            "@odata.context": "https:\/\/xxx.api.crm4.dynamics.com\/api\/data\/v9.0\/$metadata#msdyn_bookingjournals(msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode)",
            "value": [
            {
                "@odata.etag": "W\/\"3935842\"",
                "msdyn_journaltype@OData.Community.Display.V1.FormattedValue": "Travel",
                "msdyn_journaltype": 690970002,
                "msdyn_name": "Wartung  - Wartung - 101 Innener Verkehrsring\/D\u00fcsseldorferstr ",
                "msdyn_starttime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:11",
                "msdyn_starttime": "2018-11-13T13:11:58Z",
                "msdyn_endtime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:15",
                "msdyn_endtime": "2018-11-13T13:15:58Z",
                "msdyn_duration@OData.Community.Display.V1.FormattedValue": "4",
                "msdyn_duration": 4,
                "foc_sapstatisticalkeyfigurecod": "",
                "statecode@OData.Community.Display.V1.FormattedValue": "Active",
                "statecode": 0,
                "msdyn_bookingjournalid": "cb6d62ee-49e7-e811-a958-000d3a29fea4"
            },
            {
                "@odata.etag": "W\/\"3935846\"",
                "msdyn_journaltype@OData.Community.Display.V1.FormattedValue": "Working Hours",
                "msdyn_journaltype": 690970000,
                "msdyn_name": "Wartung  - Wartung - 101 Innener Verkehrsring\/D\u00fcsseldorferstr ",
                "msdyn_starttime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:15",
                "msdyn_starttime": "2018-11-13T13:15:58Z",
                "msdyn_endtime@OData.Community.Display.V1.FormattedValue": "13.11.2018 13:20",
                "msdyn_endtime": "2018-11-13T13:20:57Z",
                "msdyn_duration@OData.Community.Display.V1.FormattedValue": "4",
                "msdyn_duration": 4,
                "foc_sapstatisticalkeyfigurecod": "",
                "statecode@OData.Community.Display.V1.FormattedValue": "Active",
                "statecode": 0,
                "msdyn_bookingjournalid": "fe6d62ee-49e7-e811-a958-000d3a29fea4"
            }
            ],
            "@odata.nextLink": "https:\/\/xxx.api.crm4.dynamics.com\/api\/data\/v9.0\/bookableresourcebookings(b0fe5834-45e7-e811-a958-000d3a29fb7a)\/msdyn_bookableresourcebooking_msdyn_bookingjournal_Booking?$select=msdyn_journaltype,msdyn_name,msdyn_starttime,msdyn_endtime,msdyn_duration,foc_sapstatisticalkeyfigurecod,statecode&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%2520parentEntityId%253d%2522b0fe5834-45e7-e811-a958-000d3a29fb7a%2522%2520parentAttributeName%253d%2522msdyn_booking%2522%2520parentEntityObjectTypeCode%253d%25221145%2522%253e%253cmsdyn_bookingjournalid%2520last%253d%2522%257bFE6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520first%253d%2522%257bCB6D62EE-49E7-E811-A958-000D3A29FEA4%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20\/%3E"
        }
    ]]>
</data>

and following xslt was applied to convert above to XML

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs math" version="3.0">
    <xsl:output indent="yes" />
    <xsl:template match="data">
        <xsl:copy-of select="json-to-xml(.)/*" />
    </xsl:template>
</xsl:stylesheet>
  • 1
    It is not clear what your input format is (some JSON you have not shown, the XML you have shown in your question as your "map") and what you want to do with that input. Which software, which code exactly do you use that gives you the error "Unable to generate the XML document using the provided XML/XSL input.". If all you need is to remove an `@` sign from a string then both `replace(@key', '@', '')` and `translate(@key, '@', '')` should suffice. – Martin Honnen Jan 10 '19 at 11:09
  • Copy / pasting the XML and XSLT into https://www.freeformatter.com/xsl-transformer.html shows the issue. I clarified the question by adding the initial context. Can you add your suggestion into the XSLT where I'm converting the XML map with keys? – user2215655 Jan 10 '19 at 11:26
  • 1
    `` tries to compute an element name, if you get an error because of `@` characters in the `@key` expression you can use `` to avoid the error and create result elements based on the `key` attribute, but with any `@`s stripped. – Martin Honnen Jan 10 '19 at 11:41
  • Thanks! Note: small typo, the ' after key has to be removed. I created a fiddle, https://xsltfiddle.liberty-development.net/bnnZVy . The XSLT removes the container separating the different output values. Can this be added? – user2215655 Jan 10 '19 at 12:36
  • 1
    If you want to preserve the input structure and just transform the elements for which you have written that template you have shown then in XSLT 3 the right way is to use `` (https://xsltfiddle.liberty-development.net/bnnZVy/1). But the input elements are in that particular namespace while your current template creates elements in no namespace and so far you have not really explained which result format you want so that suggestion results in elements in different namespaces, probably not what you want. – Martin Honnen Jan 10 '19 at 12:52
  • The generated XML will be used for parsing in Groovy script, performing timestamp calculations / aggregations. – user2215655 Jan 10 '19 at 13:11
  • The shallow-copy results in empty namespace elements xmlns="". Is it correct to skip the shallow-copy and strip all namespaces using ` `, see https://xsltfiddle.liberty-development.net/bnnZVy/3 – user2215655 Jan 16 '19 at 16:40

1 Answers1

1

Just change

<xsl:element name="{@key}">

to

<xsl:element name="{translate(@key, '@', '')}"> 

or perhaps

<xsl:element name="{translate(@key, '@', '_')}">

Of course, there is no guarantee that this is enough to ensure that you end up with a valid XML element name. But it seems that the "@" signs are the only obvious thing you need to fix in this particular data.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • What is the best way to remove empty / all namespace elements like xmlns="". Is it correct to skip the shallow-copy and strip all namespaces using ` `, see https://xsltfiddle.liberty-development.net/bnnZVy/3 – user2215655 Jan 17 '19 at 09:56
  • Namespace undeclarations like `xmlns=""` appear in your output if you generate an element in no-namespace whose parent is in some other namespace. The way to avoid the namespace undeclaration is to put all the elements you generate into the correct namespace. – Michael Kay Jan 17 '19 at 16:23
  • The initial process is transforming odata to XML via json-to-xml() , see https://xsltfiddle.liberty-development.net/6r5Gh2B/1. This puts the `` namespace in place. Next, `` is used, introducing the empty namespaces, see https://xsltfiddle.liberty-development.net/jyRYYhT . Can you update the fiddle to fix this in the transformation? Thanks for your help – user2215655 Jan 21 '19 at 09:49