【问题标题】:Filter XML based on node value and convert to JSON using XSLT根据节点值过滤 XML 并使用 XSLT 转换为 JSON
【发布时间】:2016-08-19 10:48:00
【问题描述】:

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

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

【问题讨论】:

  • 您使用的是哪个版本的 Saxon?在 XSLT 和 XPath 3.0/3.1 中有一些对 XML 到 JSON 和 JSON 序列化的新支持,因此您可能可以在 9.6 或 9.7 中使用它,具体取决于版本和版本。
  • @MartinHonnen 我正在使用Saxon HE 9.6.0.7。我必须研究 Xpath 3.0 的新功能。

标签: json xml xslt xslt-2.0 saxon


【解决方案1】:

我已尝试将您的代码简化为

<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 输入转换为该函数所需的 input format 后使用函数 xml-to-json (https://www.w3.org/TR/xslt-30/#func-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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-16
    • 2018-05-31
    • 1970-01-01
    • 2020-09-14
    • 2013-02-17
    • 2019-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多