根据节点值过滤XML并使用XSLT转换为JSON

时间:2016-08-19 10:48:01

标签: json xml xslt xslt-2.0 saxon

我有一个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 &lt; $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处理器。

我按预期得到了结果。有没有其他方法可以获得理想的结果?建议表示赞赏。

1 个答案:

答案 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中,您可以构建mapsarrays,您可以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)那个功能:

: