3

I have a XSLT that works the way I want when it outputs XML, however I would like to change the XSLT it to output JSON instead. The problem appears to me with the inner most for-each loop, but I'm not sure. If there is way to make this more efficient I'm also interested in your suggestions.

Sample input XML

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <Metric measType="1526727075"
           measResult="0"
           endTime="2016-08-25T04:30:00-07:00"
           measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
           Element_Type="ENODEB"
           Key1="LTHBC0126858"
           TableName="HH_ENODEB"
           ColumnName="H1526727075"
           H1526727075="0"/>
   <Metric measType="1526727076"
           measResult="0"
           endTime="2016-08-25T04:30:00-07:00"
           measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
           Element_Type="ENODEB"
           Key1="LTHBC0126858"
           TableName="HH_ENODEB"
           ColumnName="H1526727076"
           H1526727076="0"/>
   <Metric measType="1526727077"
           measResult="0"
           endTime="2016-08-25T04:30:00-07:00"
           measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
           Element_Type="ENODEB"
           Key1="LTHBC0126858"
           TableName="HH_ENODEB"
           ColumnName="H1526727077"
           H1526727077="0"/>
</root>

This is the XSLT that outputs XML and works the way I expect

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <root>
            <xsl:for-each-group select="Metric" group-by="@measObjLdn">
                <xsl:sort select="current-grouping-key()"/>
                <xsl:variable name="curr_key" select="current-grouping-key()"/>
                <xsl:for-each-group select="current-group()" group-by="@TableName">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:if test="current-grouping-key() != ''">
                        <Table TableName="{current-grouping-key()}">
                            <xsl:for-each select="current-group()">
                                <xsl:attribute name="Stamp">
                                    <xsl:value-of select="@endTime"/>
                                </xsl:attribute>
                                <xsl:attribute name="measObjLdn">
                                    <xsl:value-of select="$curr_key"/>
                                </xsl:attribute>
                                <xsl:attribute name="Element_Type">
                                    <xsl:value-of select="@Element_Type"/>
                                </xsl:attribute>
                                <xsl:attribute name="Key1">
                                    <xsl:value-of select="@Key1"/>
                                </xsl:attribute>
                                <xsl:for-each select="@*">
                                    <xsl:if test="starts-with(name(), 'H')">
                                        <xsl:attribute name="{name()}">
                                            <xsl:value-of select="number(.)"/>
                                        </xsl:attribute>
                                    </xsl:if>
                                </xsl:for-each>
                            </xsl:for-each>
                        </Table>
                    </xsl:if>
                </xsl:for-each-group>
            </xsl:for-each-group>
        </root>
    </xsl:template>
</xsl:stylesheet>

This is my JSON output code, but not working as expected.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    <xsl:output method="text" encoding="utf-8"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:text>{"root":{</xsl:text>
        <xsl:for-each-group select="Metric" group-by="@TableName">
            <xsl:sort select="current-grouping-key()"/>
            <xsl:if test="current-grouping-key() != ''">
                <xsl:text>"Table":[</xsl:text>
                <xsl:text>{"TableName":"</xsl:text>
                <xsl:value-of select="current-grouping-key()"/>
                <!--<Table TableName="{current-grouping-key()}">-->
                <xsl:text>",</xsl:text>
                <xsl:for-each-group select="current-group()" group-by="@measObjLdn">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:for-each select="current-group()">
                        <xsl:text>"Stamp":"</xsl:text>
                        <xsl:value-of select="@endTime"/>
                        <xsl:text>",</xsl:text>
                        <xsl:text>"measObjLdn":"</xsl:text>
                        <xsl:value-of select="current-grouping-key()"/>
                        <xsl:text>",</xsl:text>
                        <xsl:text>"Element_Type":"</xsl:text>
                        <xsl:value-of select="@Element_Type"/>
                        <xsl:text>",</xsl:text>
                        <xsl:text>"Key1":"</xsl:text>
                        <xsl:value-of select="@Key1"/>
                        <xsl:text>",</xsl:text>
                        <xsl:for-each select="@attribute()">
                            <xsl:if test="starts-with(name(), 'H')">
                                <xsl:text>"</xsl:text>
                                <xsl:value-of select="name()"/>
                                <xsl:text>":"</xsl:text>
                                <xsl:value-of select="number(.)"/>
                                <xsl:text>"</xsl:text>
                                <xsl:if test="position() != last()">
                                    <xsl:text>,</xsl:text>
                                </xsl:if>
                            </xsl:if>
                        </xsl:for-each>
                    </xsl:for-each>
                </xsl:for-each-group>
                <!--</Table>-->
                <xsl:text>}</xsl:text>
                <xsl:if test="position() != last()">
                    <xsl:text>,</xsl:text>
                </xsl:if>
            </xsl:if>
        </xsl:for-each-group>
        <xsl:text>}</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Good point, so the XML version groups all the attributes for the same table together; Given the sample above the XML output is as follows (some reformatting applied for clarity):

<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <Table
    TableName="HH_ENODEB" 
    Stamp="2016-08-25T04:30:00-07:00"
    measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7" 
    Element_Type="ENODEB" 
    Key1="LTHBC0126858" 
    H1526727075="0" 
    H1526727076="0" 
    H1526727077="0"/>
</root>

JSON example output is not a JSON version of the XML version which I expected (some whitespace reformatting applied for clarity).

{
  "root": {
    "Table": [
      {
      "TableName":"HH_ENODEB",
      "Stamp":"2016-08-25T04:30:00-07:00",
      "measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
      "Element_Type":"ENODEB",
      "Key1":"LTHBC0126858",
      "H1526727075":"0"

      "Stamp":"2016-08-25T04:30:00-07:00",
      "measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
      "Element_Type":"ENODEB",
      "Key1":"LTHBC0126858",
      "H1526727076":"0"

      "Stamp":"2016-08-25T04:30:00-07:00",
      "measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
      "Element_Type":"ENODEB",
      "Key1":"LTHBC0126858",
      "H1526727077":"0"
    }}

(The above is not even well-formed JSON)

2
  • You don't say how it fails... Commented Sep 7, 2016 at 21:02
  • This is want the JSON version outputs, it appears to keep looping through all the nodes rather then grouping them together. Commented Sep 8, 2016 at 1:52

1 Answer 1

6

You didn't actually say what your expected JSON should look like, but based on your XML, I am assuming it should look like this

{
  "root":
  {
    "Table":
    [
      {
        "TableName":"HH_ENODEB",
        "Stamp":"2016-08-25T04:30:00-07:00",
        "measObjLdn":"HH_ENODEB",
        "Element_Type":"ENODEB",
        "Key1":"LTHBC0126858",
        "H1526727075":"0",
        "H1526727076":"0",
        "H1526727077":"0"
      }
    ]
  }
}

The issue is that in the XSLT that produces the XML output, you have the creation of the xsl:attributes within the statement <xsl:for-each select="current-group()">, but that means you are repeatedly outputting the same attribute names for a single Table element. In this case, XSLT will just replace any existing attribute with the latest one created, so you don't notice what is going on.

When outputting JSON (or rather, when outputting text, which just happens to be in JSON format), you do end up with the repeated attributes as it is just text.

The solution is to move the creation of the main attributes outside the inner loop.

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:text>{&#10;  "root":&#10;  {&#10;</xsl:text>
            <xsl:for-each-group select="Metric" group-by="@measObjLdn">
                <xsl:sort select="current-grouping-key()"/>
                <xsl:variable name="curr_key" select="current-grouping-key()"/>
                <xsl:text>    "Table":&#10;    [&#10;</xsl:text>
                <xsl:for-each-group select="current-group()" group-by="@TableName">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:if test="current-grouping-key() != ''">
                        <xsl:text>      {&#10;        "TableName":"</xsl:text>
                        <xsl:value-of select="current-grouping-key()"/>
                        <xsl:text>",&#10;</xsl:text>
                        <xsl:text>        "Stamp":"</xsl:text>
                        <xsl:value-of select="@endTime"/>
                        <xsl:text>",&#10;</xsl:text>
                        <xsl:text>        "measObjLdn":"</xsl:text>
                        <xsl:value-of select="$curr_key"/>
                        <xsl:text>",&#10;</xsl:text>
                        <xsl:text>        "Element_Type":"</xsl:text>
                        <xsl:value-of select="@Element_Type"/>
                        <xsl:text>",&#10;</xsl:text>
                        <xsl:text>        "Key1":"</xsl:text>
                        <xsl:value-of select="@Key1"/>
                        <xsl:text>"</xsl:text>
                        <xsl:for-each select="current-group()">
                            <xsl:for-each select="@*[starts-with(name(), 'H')]">
                                <xsl:text>,&#10;</xsl:text>
                                <xsl:text>        "</xsl:text>
                                <xsl:value-of select="name()"/>
                                <xsl:text>":"</xsl:text>
                                <xsl:value-of select="number(.)"/>
                                <xsl:text>"</xsl:text>
                            </xsl:for-each>
                        </xsl:for-each>
                        <xsl:text>&#10;      }&#10;</xsl:text>
                    </xsl:if>
                </xsl:for-each-group>
                <xsl:text>    ]&#10;</xsl:text>
            </xsl:for-each-group>
        <xsl:text>  }&#10;}</xsl:text>
    </xsl:template>
</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for explaining why the different behavior, to me that's more important lesson than just working code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.