我有一个XML文档,我必须根据节点的值进行过滤,然后必须返回匹配节点的父节点及其所有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处理器。
我按预期得到了结果。有没有其他方法可以获得理想的结果?建议表示赞赏。
答案 0 :(得分:3)
我试图将代码简化为
<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和arrays,您可以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输入转换为https://www.w3.org/TR/xslt-30/#func-xml-to-json所需的xml-to-json
后使用函数<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>
(input format)那个功能:
: