Reputation: 103
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)
Upvotes: 3
Views: 14469
Reputation: 70618
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>{ "root": { </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": [ </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> { "TableName":"</xsl:text>
<xsl:value-of select="current-grouping-key()"/>
<xsl:text>", </xsl:text>
<xsl:text> "Stamp":"</xsl:text>
<xsl:value-of select="@endTime"/>
<xsl:text>", </xsl:text>
<xsl:text> "measObjLdn":"</xsl:text>
<xsl:value-of select="$curr_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="current-group()">
<xsl:for-each select="@*[starts-with(name(), 'H')]">
<xsl:text>, </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> } </xsl:text>
</xsl:if>
</xsl:for-each-group>
<xsl:text> ] </xsl:text>
</xsl:for-each-group>
<xsl:text> } }</xsl:text>
</xsl:template>
</xsl:stylesheet>
Upvotes: 6