根据节点值筛选 XML 并使用 XSLT 转换为 JSON
Filter XML based on node value and convert to JSON using XSLT
我有一个 XML 文档,我必须根据节点的值进行过滤,然后 return 匹配节点的父节点及其所有子节点 JSON格式。
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<col-name name="col1Name" />
<col-name name="col2Name" />
<row>
<col>Test1</col>
<col>Test2</col>
</row>
<row>
<col>Test3</col>
<col>Test4</col>
</row>
</root>
XSL:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/">
{
"filtered-result": [
<xsl:for-each select="//row[col[normalize-space(text()) = 'Test2']]">
<xsl:variable name="columnCount" select="count(./*)" />
{
<xsl:for-each select="./*">
<xsl:variable name="columnIndex" select="position()" />
"<xsl:value-of select="normalize-space(//col-name[$columnIndex]/@name)" />": "<xsl:value-of select="." />"<xsl:if test="$columnIndex < $columnCount">,</xsl:if>
</xsl:for-each>
}<xsl:if test="./following-sibling::*">,</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
</xsl:transform>
输出:
{
"filtered-result": [
{
"col1Name": "Test1",
"col2Name": "Test2"
}
]
}
我正在使用 SAXON 作为 XSLT 处理器。
我得到了预期的结果。有没有其他方法可以得到想要的结果?赞赏建议。
我已尝试将您的代码简化为
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:key name="col" match="col-name">
<xsl:number/>
</xsl:key>
<xsl:template match="/">
{
"filtered-result": [
<xsl:for-each select="//row[col[normalize-space(.) = 'Test2']]">
{
<xsl:for-each select="*">
"<xsl:value-of select="key('col', string(position()))/@name" />": "<xsl:value-of select="." />"<xsl:if test="position() lt last()">,</xsl:if>
</xsl:for-each>
}<xsl:if test="position() lt last()">,</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
</xsl:transform>
你的解决方案和我的简化尝试都遇到了一个问题,即列中的任何字符都需要在 JSON 值中转义,例如双引号 "
会中断输出结果。
因此我认为最好依靠考虑到此类问题并根据需要转义任何字符的转换。
在支持 XPath 3.1 的 XSLT 3.0 中,您可以构造 maps and arrays and you can serialize them as JSON,这是我尝试在您的问题上使用这些功能的尝试:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="xs math map array"
version="3.0">
<xsl:output method="json" indent="yes"/>
<xsl:key name="col" match="col-name">
<xsl:number/>
</xsl:key>
<xsl:template match="root">
<xsl:sequence select="map { 'filtered-result' : array:join(row[col = 'Test2']/[map:merge(col/map:entry(key('col', string(position()))/@name, string())) ]) }"/>
</xsl:template>
</xsl:stylesheet>
Saxon 9.7 EE,当 运行 XSLT 3.0 代码针对输入样本
<?xml version="1.0" encoding="UTF-8"?>
<root>
<col-name name="col1Name" />
<col-name name="col2Name" />
<row>
<col>Test1</col>
<col>Test2</col>
</row>
<row>
<col>Test3</col>
<col>Test4</col>
</row>
<row>
<col>Test5</col>
<col>Test2"</col>
</row>
</root>
输出
{
"filtered-result": [
{
"col1Name":"Test1",
"col2Name":"Test2"
},
{
"col1Name":"Test5",
"col2Name":"Test2\""
}
]
}
作为第三个选项,它也需要 XSLT 3.0,但在 Saxon 9.7 HE 中可用,您可以使用函数 xml-to-json
(该函数需要 https://www.w3.org/TR/xslt-30/#func-xml-to-json) after transforming your XML input to the input format:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs" version="3.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="col" match="col-name">
<xsl:variable name="index" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:sequence select="$index"/>
</xsl:key>
<xsl:template match="/">
<xsl:variable name="json-doc">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-doc, map{ 'indent': true()})"/>
</xsl:template>
<xsl:template match="root">
<map>
<array key="filtered-results">
<xsl:apply-templates select="row[col[normalize-space(.) = 'Test2']]"/>
</array>
</map>
</xsl:template>
<xsl:template match="row">
<map>
<xsl:apply-templates/>
</map>
</xsl:template>
<xsl:template match="col">
<string key="{key('col', position())/@name}">
<xsl:value-of select="."/>
</string>
</xsl:template>
</xsl:stylesheet>
我有一个 XML 文档,我必须根据节点的值进行过滤,然后 return 匹配节点的父节点及其所有子节点 JSON格式。
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<col-name name="col1Name" />
<col-name name="col2Name" />
<row>
<col>Test1</col>
<col>Test2</col>
</row>
<row>
<col>Test3</col>
<col>Test4</col>
</row>
</root>
XSL:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/">
{
"filtered-result": [
<xsl:for-each select="//row[col[normalize-space(text()) = 'Test2']]">
<xsl:variable name="columnCount" select="count(./*)" />
{
<xsl:for-each select="./*">
<xsl:variable name="columnIndex" select="position()" />
"<xsl:value-of select="normalize-space(//col-name[$columnIndex]/@name)" />": "<xsl:value-of select="." />"<xsl:if test="$columnIndex < $columnCount">,</xsl:if>
</xsl:for-each>
}<xsl:if test="./following-sibling::*">,</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
</xsl:transform>
输出:
{
"filtered-result": [
{
"col1Name": "Test1",
"col2Name": "Test2"
}
]
}
我正在使用 SAXON 作为 XSLT 处理器。
我得到了预期的结果。有没有其他方法可以得到想要的结果?赞赏建议。
我已尝试将您的代码简化为
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:key name="col" match="col-name">
<xsl:number/>
</xsl:key>
<xsl:template match="/">
{
"filtered-result": [
<xsl:for-each select="//row[col[normalize-space(.) = 'Test2']]">
{
<xsl:for-each select="*">
"<xsl:value-of select="key('col', string(position()))/@name" />": "<xsl:value-of select="." />"<xsl:if test="position() lt last()">,</xsl:if>
</xsl:for-each>
}<xsl:if test="position() lt last()">,</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
</xsl:transform>
你的解决方案和我的简化尝试都遇到了一个问题,即列中的任何字符都需要在 JSON 值中转义,例如双引号 "
会中断输出结果。
因此我认为最好依靠考虑到此类问题并根据需要转义任何字符的转换。
在支持 XPath 3.1 的 XSLT 3.0 中,您可以构造 maps and arrays and you can serialize them as JSON,这是我尝试在您的问题上使用这些功能的尝试:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="xs math map array"
version="3.0">
<xsl:output method="json" indent="yes"/>
<xsl:key name="col" match="col-name">
<xsl:number/>
</xsl:key>
<xsl:template match="root">
<xsl:sequence select="map { 'filtered-result' : array:join(row[col = 'Test2']/[map:merge(col/map:entry(key('col', string(position()))/@name, string())) ]) }"/>
</xsl:template>
</xsl:stylesheet>
Saxon 9.7 EE,当 运行 XSLT 3.0 代码针对输入样本
<?xml version="1.0" encoding="UTF-8"?>
<root>
<col-name name="col1Name" />
<col-name name="col2Name" />
<row>
<col>Test1</col>
<col>Test2</col>
</row>
<row>
<col>Test3</col>
<col>Test4</col>
</row>
<row>
<col>Test5</col>
<col>Test2"</col>
</row>
</root>
输出
{
"filtered-result": [
{
"col1Name":"Test1",
"col2Name":"Test2"
},
{
"col1Name":"Test5",
"col2Name":"Test2\""
}
]
}
作为第三个选项,它也需要 XSLT 3.0,但在 Saxon 9.7 HE 中可用,您可以使用函数 xml-to-json
(该函数需要 https://www.w3.org/TR/xslt-30/#func-xml-to-json) after transforming your XML input to the input format:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs" version="3.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="col" match="col-name">
<xsl:variable name="index" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:sequence select="$index"/>
</xsl:key>
<xsl:template match="/">
<xsl:variable name="json-doc">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-doc, map{ 'indent': true()})"/>
</xsl:template>
<xsl:template match="root">
<map>
<array key="filtered-results">
<xsl:apply-templates select="row[col[normalize-space(.) = 'Test2']]"/>
</array>
</map>
</xsl:template>
<xsl:template match="row">
<map>
<xsl:apply-templates/>
</map>
</xsl:template>
<xsl:template match="col">
<string key="{key('col', position())/@name}">
<xsl:value-of select="."/>
</string>
</xsl:template>
</xsl:stylesheet>