XSLT XML 到 JSON 问题
XSLT XML to JSON issue
我正在尝试使用 XSLT 将 XML 更改为 JSON。它实际上是一个非常简单的模式,但我修改使用的 XSLT 比我需要的更复杂,因为除了顶级的之外,我不需要漂亮的打印或数组。
来源XML:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<GR CO="B" Date="2022-03-15" Zone="ASDF" Truck="1TR"></GR>
<BS CO="A" Date="2022-03-14" Zone="ASDF" Truck="BT1"/>
<GR CO="A" Date="2022-03-14" Zone="QWER" Truck="2TK"></GR>
</r>
所有属性都将成为一个 JSON 字符串元素,后跟一个逗号。然后在名为 Line 的末尾附加一个原始元素名称的值。最好也按日期属性排序。
所需输出的漂亮打印,但不需要白色 space:
{
"Routes": [
{
"CO": "A",
"Date": "2022-03-14",
"Zone": "QWER",
"Truck": "BT1",
"Line": "BS"
},
{
"CO": "A",
"Date": "2022-03-14",
"Zone": "ASDF",
"Truck": "2TK",
"Line": "GR"
},
{
"CO": "B",
"Date": "2022-03-15",
"Zone": "ASDF",
"Truck": "1TR",
"Line": "GR"
}
]
}
这就是我正在尝试的方法,但我无法让它工作:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>
<xsl:template match="/r">
<xsl:text>{"Routes": [</xsl:text>
<xsl:apply-templates select="." mode="detect">
<xsl:sort select="./@Date" />
</xsl:apply-templates>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="@*" priority="1">
<xsl:text>Found Attrib</xsl:text>
</xsl:template>
<xsl:template match="*" priority="2" mode="detect">
<xsl:text>{</xsl:text>
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates select="." mode="obj-content" />
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates select="." mode="obj-content" />
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/><xsl:text>" : [</xsl:text>
<xsl:apply-templates select="." mode="obj-content" /><xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(@*) > 0">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : <xsl:apply-templates select="." mode="obj-content" />
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/><xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
<xsl:text>"Line": "</xsl:text><xsl:value-of select="local-name()"/><xsl:text>"
</xsl:text>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates select="@*" mode="attr" />
<xsl:if test="count(@*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates select="./*" mode="detect" />
<xsl:if test="count(child::*) = 0 and text() and not(@*)">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/><xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and @*">
<xsl:text>"text" : "</xsl:text><xsl:value-of select="text()"/><xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="@*" mode="attr">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="."/><xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/@TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))"><xsl:copy-of select="$pText"/></xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
首先,您没有说明要使用哪个版本的 XSLT,所以这是一个 XSLT 3.0 解决方案:
<xsl:stylesheet version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="json" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/r">
<xsl:map>
<xsl:variable name="content" as="map(*)*">
<xsl:apply-templates>
<xsl:sort select="xs:date(@Date)"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:map-entry key="'Routes'" select="array{$content}"/>
</xsl:map>
</xsl:template>
<xsl:template match="r/*">
<xsl:sequence select="map{
'CO': string(@CO),
'Date': string(@Date),
'Zone': string(@Zone),
'Truck': string(@Truck),
'Line':local-name()}"/>
</xsl:template>
</xsl:stylesheet>
对于 Saxon,这会产生输出
{ "Routes": [
{ "CO": "A","Zone": "ASDF","Date": "2022-03-14","Line": "BS","Truck": "BT1" },
{ "CO": "A","Zone": "QWER","Date": "2022-03-14","Line": "GR","Truck": "2TK" },
{ "CO": "B","Zone": "ASDF","Date": "2022-03-15","Line": "GR","Truck": "1TR" }
] }
使用 XSLT 1.0 几乎没有任何困难,而且比你做的要容易得多(我真的完全无法理解你的复杂逻辑)。
试试这个:
<xsl:stylesheet version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/r">
{ "Routes": [
<xsl:for-each select="*">
<xsl:sort select="@Date"/>
<xsl:if test="position() != 1">, </xsl:if>
{"CO": "<xsl:value-of select="@CO"/>",
"Date": "<xsl:value-of select="@Date"/>",
"Zone": "<xsl:value-of select="@Zone"/>",
"Truck": "<xsl:value-of select="@Truck"/>",
"Line": "<xsl:value-of select="local-name()"/>"}
</xsl:for-each>
]}
</xsl:template>
</xsl:stylesheet>
输出是:
{ "Routes": [
{"CO": "A",
"Date": "2022-03-14",
"Zone": "ASDF",
"Truck": "BT1",
"Line": "BS"}
,
{"CO": "A",
"Date": "2022-03-14",
"Zone": "QWER",
"Truck": "2TK",
"Line": "GR"}
,
{"CO": "B",
"Date": "2022-03-15",
"Zone": "ASDF",
"Truck": "1TR",
"Line": "GR"}
]}
我正在尝试使用 XSLT 将 XML 更改为 JSON。它实际上是一个非常简单的模式,但我修改使用的 XSLT 比我需要的更复杂,因为除了顶级的之外,我不需要漂亮的打印或数组。
来源XML:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<GR CO="B" Date="2022-03-15" Zone="ASDF" Truck="1TR"></GR>
<BS CO="A" Date="2022-03-14" Zone="ASDF" Truck="BT1"/>
<GR CO="A" Date="2022-03-14" Zone="QWER" Truck="2TK"></GR>
</r>
所有属性都将成为一个 JSON 字符串元素,后跟一个逗号。然后在名为 Line 的末尾附加一个原始元素名称的值。最好也按日期属性排序。
所需输出的漂亮打印,但不需要白色 space:
{
"Routes": [
{
"CO": "A",
"Date": "2022-03-14",
"Zone": "QWER",
"Truck": "BT1",
"Line": "BS"
},
{
"CO": "A",
"Date": "2022-03-14",
"Zone": "ASDF",
"Truck": "2TK",
"Line": "GR"
},
{
"CO": "B",
"Date": "2022-03-15",
"Zone": "ASDF",
"Truck": "1TR",
"Line": "GR"
}
]
}
这就是我正在尝试的方法,但我无法让它工作:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>
<xsl:template match="/r">
<xsl:text>{"Routes": [</xsl:text>
<xsl:apply-templates select="." mode="detect">
<xsl:sort select="./@Date" />
</xsl:apply-templates>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="@*" priority="1">
<xsl:text>Found Attrib</xsl:text>
</xsl:template>
<xsl:template match="*" priority="2" mode="detect">
<xsl:text>{</xsl:text>
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates select="." mode="obj-content" />
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates select="." mode="obj-content" />
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/><xsl:text>" : [</xsl:text>
<xsl:apply-templates select="." mode="obj-content" /><xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(@*) > 0">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : <xsl:apply-templates select="." mode="obj-content" />
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/><xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
<xsl:text>"Line": "</xsl:text><xsl:value-of select="local-name()"/><xsl:text>"
</xsl:text>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates select="@*" mode="attr" />
<xsl:if test="count(@*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates select="./*" mode="detect" />
<xsl:if test="count(child::*) = 0 and text() and not(@*)">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/><xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and @*">
<xsl:text>"text" : "</xsl:text><xsl:value-of select="text()"/><xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="@*" mode="attr">
<xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="."/><xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/@TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))"><xsl:copy-of select="$pText"/></xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
首先,您没有说明要使用哪个版本的 XSLT,所以这是一个 XSLT 3.0 解决方案:
<xsl:stylesheet version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="json" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/r">
<xsl:map>
<xsl:variable name="content" as="map(*)*">
<xsl:apply-templates>
<xsl:sort select="xs:date(@Date)"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:map-entry key="'Routes'" select="array{$content}"/>
</xsl:map>
</xsl:template>
<xsl:template match="r/*">
<xsl:sequence select="map{
'CO': string(@CO),
'Date': string(@Date),
'Zone': string(@Zone),
'Truck': string(@Truck),
'Line':local-name()}"/>
</xsl:template>
</xsl:stylesheet>
对于 Saxon,这会产生输出
{ "Routes": [
{ "CO": "A","Zone": "ASDF","Date": "2022-03-14","Line": "BS","Truck": "BT1" },
{ "CO": "A","Zone": "QWER","Date": "2022-03-14","Line": "GR","Truck": "2TK" },
{ "CO": "B","Zone": "ASDF","Date": "2022-03-15","Line": "GR","Truck": "1TR" }
] }
使用 XSLT 1.0 几乎没有任何困难,而且比你做的要容易得多(我真的完全无法理解你的复杂逻辑)。
试试这个:
<xsl:stylesheet version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/r">
{ "Routes": [
<xsl:for-each select="*">
<xsl:sort select="@Date"/>
<xsl:if test="position() != 1">, </xsl:if>
{"CO": "<xsl:value-of select="@CO"/>",
"Date": "<xsl:value-of select="@Date"/>",
"Zone": "<xsl:value-of select="@Zone"/>",
"Truck": "<xsl:value-of select="@Truck"/>",
"Line": "<xsl:value-of select="local-name()"/>"}
</xsl:for-each>
]}
</xsl:template>
</xsl:stylesheet>
输出是:
{ "Routes": [
{"CO": "A",
"Date": "2022-03-14",
"Zone": "ASDF",
"Truck": "BT1",
"Line": "BS"}
,
{"CO": "A",
"Date": "2022-03-14",
"Zone": "QWER",
"Truck": "2TK",
"Line": "GR"}
,
{"CO": "B",
"Date": "2022-03-15",
"Zone": "ASDF",
"Truck": "1TR",
"Line": "GR"}
]}