XSLT处理具有多个名称空间的XML

时间:2019-05-01 22:12:43

标签: xml-namespaces xslt-3.0

我有带有多个名称空间的XML,但是所有元素都相同。我需要将此XML转换为JSON,但是我不确定如何动态地传递/更改名称空间,而不用不同的名称空间重复相同的XSLT代码。

因此,我仅在我定义的名称空间的输出中获取数据。

下面是我的示例XML-

<?xml version="1.0" encoding="utf-8"?>
<root>
    <wd:Report_Data xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction">
        <wd:Report_Entry>
            <wd:company>TESTCOMPANY</wd:company>
            <wd:revenue_stream>X</wd:revenue_stream>
            <wd:customer_id>XCUSTOMER</wd:customer_id>
            <wd:invoice_id>201900000035</wd:invoice_id>
            <wd:post_date>2019-05-01</wd:post_date>
            <wd:initiatedby>Test Data</wd:initiatedby>
            <wd:amount>-100</wd:amount>
            <wd:trans_date>2019-04-22</wd:trans_date>
            <wd:legacy>false</wd:legacy>
            <wd:exported>2019-05-01T12:13:02.773-07:00</wd:exported>
            <wd:reason>Credit Invoice</wd:reason>
        </wd:Report_Entry>
    </wd:Report_Data>
    <wd:Report_Data xmlns:wd="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction">
        <wd:Report_Entry>
            <wd:company>TESTCOMPANY</wd:company>                
            <wd:revenue_stream>X</wd:revenue_stream>            
            <wd:customer_id>XCUSTOMER</wd:customer_id>
            <wd:invoice_id>201900000020</wd:invoice_id>
            <wd:post_date>2019-05-01</wd:post_date>
            <wd:amount>30</wd:amount>
            <wd:trans_date>2019-04-01</wd:trans_date>
            <wd:legacy>false</wd:legacy>
            <wd:exported>2019-05-01T12:13:03.030-07:00</wd:exported>
            <wd:reason>Disputed Amount</wd:reason>
        </wd:Report_Entry>
    </wd:Report_Data>
</root>
Below is the XSLT i have created - 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction" xmlns:wd1="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction" exclude-result-prefixes="xs" version="3.0">
    <xsl:mode streamable="yes" on-no-match="shallow-skip"/>
    <xsl:output method="text" encoding="UTF-8" indent="no"/>
    <xsl:template match="root/wd:Report_Data">
        <xsl:iterate select="wd:Report_Entry/copy-of()">
            <!--Define Running Totals for Statistics -->
            <xsl:param name="TotalCount" select="0"/>
            <xsl:param name="TotalAmount" select="0"/>
            <!--Write Statistics -->
            <xsl:on-completion>
                <xsl:text>{"Stats": </xsl:text>
                <xsl:text>{"Total Count": </xsl:text>
                <xsl:value-of select="$TotalCount"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"Total Amount": </xsl:text>
                <xsl:value-of select="$TotalAmount"/>
                <xsl:text>}}</xsl:text>
            </xsl:on-completion>
            <!--Write Details -->
            <xsl:text>{"id": "</xsl:text>
            <xsl:value-of select="wd:id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"company": "</xsl:text>
            <xsl:value-of select="wd:company"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"trans_type": "</xsl:text>
            <xsl:value-of select="wd:trans_type"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"revenue_stream": "</xsl:text>
            <xsl:value-of select="wd:revenue_stream"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"customer_id": "</xsl:text>
            <xsl:value-of select="wd:customer_id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"invoice_id": "</xsl:text>
            <xsl:value-of select="wd:invoice_id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"post_date": "</xsl:text>
            <xsl:value-of select="wd:post_date"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"initiatedby": "</xsl:text>
            <xsl:value-of select="wd:initiatedby"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"amount": </xsl:text>
            <xsl:value-of select="wd:amount"/>
            <xsl:text>,</xsl:text>
            <xsl:text>"trans_date": "</xsl:text>
            <xsl:value-of select="wd:trans_date"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"legacy": </xsl:text>
            <xsl:value-of select="wd:legacy"/>
            <xsl:text>,</xsl:text>
            <xsl:text>"exported": "</xsl:text>
            <xsl:value-of select="wd:exported"/>
            <xsl:text>"}</xsl:text>
            <!--Store Running Totals -->
            <xsl:next-iteration>
                <xsl:with-param name="TotalCount" select="$TotalCount + 1"/>
                <xsl:with-param name="TotalAmount" select="$TotalAmount + wd:amount"/>
            </xsl:next-iteration>
        </xsl:iterate>
    </xsl:template>
</xsl:stylesheet>
Expected Result - 
{
    "id": "",
    "company": "TESTCOMPANY",
    "trans_type": "",
    "revenue_stream": "",
    "customer_id": "XCUSTOMER",
    "invoice_id": "201900000035",
    "post_date": "2019-05-01",
    "initiatedby": "Test Data",
    "amount": -100,
    "trans_date": "2019-04-22",
    "legacy": false,
    "exported": "2019-05-01T12:13:02.773-07:00"
}
{
    "id": "",
    "company": "TESTCOMPANY",
    "trans_type": "",
    "revenue_stream": "X",
    "customer_id": "XCUSTOMER",
    "invoice_id": "201900000035",
    "post_date": "2019-05-01",
    "initiatedby": "Test Data",
    "amount": -100,
    "trans_date": "2019-04-22",
    "legacy": false,
    "exported": "2019-05-01T12:13:02.773-07:00"
} {
    "Stats": {
        "Total Count": 2,
        "Total Amount": -200
    }
}

1 个答案:

答案 0 :(得分:0)

这里是一个使用通配符选择器*:foo和累加器来生成JSON输出的示例,因为我所知道的唯一的XSLT 3流处理器是Saxon 9 EE,我还使用了扩展元素saxon:array ,因为这样可以更轻松地创建带有数组的JSON输出。在仅具有XPath 3.1数组构造函数[]array { }的纯XSLT 3中,从像xsl:apply-templates这样的XSLT指令中即时生成数组数据总是很麻烦。 >

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    extension-element-prefixes="saxon"
    exclude-result-prefixes="#all" version="3.0">

    <xsl:mode use-accumulators="#all" streamable="yes"/>

    <xsl:output method="json" indent="yes"/>

    <xsl:accumulator name="entry-count" as="xs:integer" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="*:Report_Data/*:Report_Entry" select="$value + 1"/>
    </xsl:accumulator>

    <xsl:accumulator name="amount-sum" as="xs:decimal" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="*:Report_Data/*:Report_Entry/*:amount/text()"
            select="$value + xs:decimal(.)"/>
    </xsl:accumulator>

    <xsl:template match="root">
        <xsl:map>
            <xsl:map-entry key="local-name()">
                <saxon:array>
                    <xsl:apply-templates select="*:Report_Data/*:Report_Entry"/>
                    <xsl:sequence
                        select="
                            map {
                                'Stats': map {
                                    'Total Count': accumulator-after('entry-count'),
                                    'Total Amount': accumulator-after('amount-sum')
                                }
                            }"
                    />
                </saxon:array>
            </xsl:map-entry>
        </xsl:map>
    </xsl:template>

    <xsl:template match="*:Report_Entry">
        <xsl:sequence
            select="
                map {
                    'id': string(*:id),
                    'amount': xs:decimal(*:amount)
                }"
        />
    </xsl:template>

</xsl:stylesheet>

不幸的是,在oXygen 21、9.8.0.12和9.9.1.1中提供了两个版本的Saxon 9 EE,这仅通过了9.9中的流动性分析,因此结果是

{ 
    "root": [
      {
        "amount": -100,
        "id": null
      },
      {
        "amount": 30,
        "id": null
      },
      { 
        "Stats": { 
          "Total Count": 2,
          "Total Amount": -70 } }
    ] }

当然,使用通配符*:foo的方法也可以与您的xsl:iterate方法一起使用:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction" xmlns:wd1="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction" exclude-result-prefixes="xs" version="3.0">
    <xsl:mode streamable="yes" on-no-match="shallow-skip"/>
    <xsl:output method="text" encoding="UTF-8" indent="no"/>
    <xsl:template match="root">
        <xsl:iterate select="*:Report_Data/*:Report_Entry/copy-of()">
            <!--Define Running Totals for Statistics -->
            <xsl:param name="TotalCount" select="0"/>
            <xsl:param name="TotalAmount" select="0"/>
            <!--Write Statistics -->
            <xsl:on-completion>
                <xsl:text>{"Stats": </xsl:text>
                <xsl:text>{"Total Count": </xsl:text>
                <xsl:value-of select="$TotalCount"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"Total Amount": </xsl:text>
                <xsl:value-of select="$TotalAmount"/>
                <xsl:text>}}</xsl:text>
            </xsl:on-completion>
            <!--Write Details -->
            <xsl:text>{"id": "</xsl:text>
            <xsl:value-of select="*:id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"company": "</xsl:text>
            <xsl:value-of select="*:company"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"trans_type": "</xsl:text>
            <xsl:value-of select="*:trans_type"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"revenue_stream": "</xsl:text>
            <xsl:value-of select="*:revenue_stream"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"customer_id": "</xsl:text>
            <xsl:value-of select="*:customer_id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"invoice_id": "</xsl:text>
            <xsl:value-of select="*:invoice_id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"post_date": "</xsl:text>
            <xsl:value-of select="*:post_date"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"initiatedby": "</xsl:text>
            <xsl:value-of select="*:initiatedby"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"amount": </xsl:text>
            <xsl:value-of select="*:amount"/>
            <xsl:text>,</xsl:text>
            <xsl:text>"trans_date": "</xsl:text>
            <xsl:value-of select="*:trans_date"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"legacy": </xsl:text>
            <xsl:value-of select="*:legacy"/>
            <xsl:text>,</xsl:text>
            <xsl:text>"exported": "</xsl:text>
            <xsl:value-of select="*:exported"/>
            <xsl:text>"}</xsl:text>
            <!--Store Running Totals -->
            <xsl:next-iteration>
                <xsl:with-param name="TotalCount" select="$TotalCount + 1"/>
                <xsl:with-param name="TotalAmount" select="$TotalAmount + *:amount"/>
            </xsl:next-iteration>
        </xsl:iterate>
    </xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/ej9EGcA/1