XSLT для обработки XML с несколькими пространствами имен - PullRequest
0 голосов
/ 02 мая 2019

У меня есть 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 голосов
/ 02 мая 2019

Вот пример использования селектора подстановочных знаков *:foo и аккумуляторов для получения вывода JSON, поскольку единственный потоковый процессор XSLT 3, о котором я знаю, это Saxon 9 EE. Я также использовал элемент расширения saxon:array, так как он делаетпроще создать вывод JSON с массивами.В чистом XSLT 3, где у вас есть только конструкторы массива XPath 3.1 [] и array { }, всегда немного сложно получить данные массива на лету из инструкций XSLT, таких как xsl:apply-templates:

<?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>

К сожалению, с двумя версиями Saxon 9 EE, доступными в oXygen 21, 9.8.0.12 и 9.9.1.1, это только проходит анализ стреймабельности в 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

...