基于数据帧生成具有条件输出的 XML 文件

Generating an XML file with conditional output based on a dataframe

我正在尝试生成一个基于数据帧的 XML 文件。 XML 输出的结构应包含一些一般信息,然后是交易列表,然后是一些结尾注释。棘手的一点是每种交易类型都需要不同的输出和父标签,这意味着我需要生成几个有条件执行的模板。数据看起来像这样:

secondary_info_1 transactionnumber date_transaction amount_local from_country to_country transaction_type gender
Some stuff goes here trx001 2022-01-02 2883 DE incoming_1 M
Some stuff goes here trx003 2022-01-04 857 incoming_2 M
Some stuff goes here trx004 2022-01-05 4 AT outgoing_1 M
Some stuff goes here trx006 2022-01-07 26 outgoing_2 M

所需的输出如下所示:

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<record>
    <secondary_info_1>Some stuff goes here</secondary_info_1>
    <incoming_transaction_1>
        <transaction_from>
            <transactionnumber>trxno001</transactionnumber>
            <date_transaction>2022-01-02</date_transaction>
            <amount_local>2883</amount_local>
            <from_country>DE</from_country>
        </transaction_from>
        <transaction_to>
            <personalia>
                <gender>M</gender>
            </personalia>
        </transaction_to>
    </incoming_transaction_1>
    <incoming_transaction_2>
        <transaction_from>
            <transactionnumber>trxno003</transactionnumber>
            <date_transaction>2022-01-04</date_transaction>
            <amount_local>857</amount_local>
        </transaction_from>
        <transaction_to>
            <personalia>
                <gender>M</gender>
            </personalia>
        </transaction_to>
    </incoming_transaction_2>
    <outgoing_transaction_1>
        <transaction_to>
            <personalia>
                <gender>M</gender>
            </personalia>
        </transaction_to>
        <transaction_from>
            <transactionnumber>trxno004</transactionnumber>
            <date_transaction>2022-01-05</date_transaction>
            <amount_local>4</amount_local>
            <from_country>AT</from_country>
        </transaction_from>
    </outgoing_transaction_1>
    <outgoing_transaction_2>
        <transaction_to>
            <personalia>
                <gender>M</gender>
            </personalia>
        </transaction_to>
        <transaction_from>
            <transactionnumber>trxno006</transactionnumber>
            <date_transaction>2022-01-07</date_transaction>
            <amount_local>26</amount_local>
        </transaction_from>
    </outgoing_transaction_2>
    <secondary_info_2>Some more stuff goes here</secondary_info_2>
</record>

简而言之,我们有一个交易概览,其中每个交易都附有个人资料,以及这些交易上方和下方的一些信息 (secondary_info_1/2)。模板根据 transaction_type 列中的值更改。

我发现通过在 pandas.to_xml() 函数中使用样式表参数来生成一个更静态的 XML 确实效果很好,但是我很难创建一个有条件触发的模板。我尝试修改采用 here 的方法(单元格 366 和 367,就在“XML Final Notes”之前),但我不知道如何使用 <xsl_if test="transaction_type == 'incoming_1'"> 等来触发我的不同有条件的交易模板。

有人可以帮忙吗?

编辑:所需输出的一般结构如下(上下文也作为标签中的文本添加):

<record>
    <secondary_info>
        <nested_secondary_info>A bunch of added information should go in this first section of the document</nested_secondary_info>
    <incoming_transaction_1>
        <incoming_nested_transaction_info_1>Some, but not all of the fields go here, and the structure changes based on the transaction type</incoming_nested_transaction_info_1>
    </incoming_transaction_1>
    <outgoing_transaction_1>
        <outgoing_nested_transaction_info_2>For example, the "from_country" field should only be returned for incoming_transaction_1, and "to_country" should only be returned for "outgoing_transaction_1"</outgoing_nested_transaction_info_2>
    </outgoing_transaction_1>
    <incoming_transaction_2>
        <incoming_nested_transaction_info_2>Ideally the transactions would also be sorted based on the date, but it seems that works as long as the dataframe is sorted correctly</incoming_nested_transaction_info_2>
    </incoming_transaction_2>
    <some_more_secondary_info>
        <some_more_nested_secondary_info>I would also need to add some information to the end of this document as a sibling of the transactional information</some_more_nested_secondary_info> 
    </some_more_secondary_info>
</record>

由于对输出的特定要求,无论如何编写特定模板都会很痛苦。然而,我已经能够编写一个基本的 XSLT,使我能够生成事务概述:

<xsl:stylesheet version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/data">
        <report>
            <xsl:apply-templates select="row"/>
        </report>
    </xsl:template>

    <xsl:template match="row">
        <xsl:choose>
            <xsl:when test="transaction_type='incoming_1'">
                <transaction_account_to_consumer>
                    <xsl:copy-of select="amount_local"/>
                    <t_from>
                        <xsl:copy-of select="from_country"/>
                    </t_from>
                </transaction_account_to_consumer>
            </xsl:when>
            <xsl:when test="transaction_type='incoming_2'">
                <transaction_entity_to_consumer>
                    <xsl:copy-of select="amount_local"/>
                </transaction_entity_to_consumer>
            </xsl:when>
            <xsl:when test="transaction_type='outgoing_1'">
                <transaction_consumer_to_account>
                    <t_to>
                        <xsl:copy-of select="to_country"/>
                    </t_to>
                    <xsl:copy-of select="amount_local"/>
                </transaction_consumer_to_account>
            </xsl:when>
            <xsl:when test="transaction_type='outgoing_2'">
                <transaction_consumer_to_entity>
                    <xsl:copy-of select="amount_local"/>
                </transaction_consumer_to_entity>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

此模板会膨胀到数百行(每个交易类型子模板需要 >50 个标签),这很烦人,但可以管理。然而,一个更大的问题是,虽然我可以让条件模板为我的目的工作,但我不知道如何在交易信息的上方和下方添加我的 secondary_info。

如果我在 xsl:choose 之前添加它,它只是将它附加到每个交易之上,而我希望它只附加一次,在交易列表之前。

我解决这个问题的想法是只匹配不同模板中的 first 行,然后在 ,表示 XSLT 顶部的信息看起来更像这样:

<xsl:template match="/data">
    <report>
        <xsl:apply-templates select="first_row"/>
        <xsl:apply-templates select="row"/>
        <xsl:apply-templates select="first_row"/>
    </report>
</xsl:template>

每个模板只会 select 数据框第一行中的某些字段。但是,两个“select="first_row"" 模板将 select 不同于交易概览第一行的 字段。

有什么方法可以实现吗?

可能很简单,你的测试语法有点不对,是test="transaction_type = 'incoming_1'",用=,不是==

这是一个简单的 XSLT,如果 transaction_type 为 incoming_1,则仅保留 <row>main.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="row">
        <xsl:if test="transaction_type = 'incoming_1'">
            <xsl:copy>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

当我运行这个:

df = pd.read_csv('input.csv')

print(df.to_xml(stylesheet='main.xsl'))

我得到:

<?xml version="1.0" encoding="UTF-8"?>
<data>
  <row>
    <index>0</index>
    <secondary_info_1>Some stuff goes here</secondary_info_1>
    <transactionnumber>trx001</transactionnumber>
    <date_transaction>2022-01-02</date_transaction>
    <amount_local>2883</amount_local>
    <from_country>DE</from_country>
    <to_country/>
    <transaction_type>incoming_1</transaction_type>
    <gender>M</gender>
  </row>
</data>