【问题标题】:Replace elements in XML with reference to pandas dataframe参考 pandas 数据框替换 XML 中的元素
【发布时间】:2020-08-08 05:39:39
【问题描述】:

我正在使用 lxml 读取我的 xml 文件:

tree = etree.parse(r'C:\Users\xxx\Desktop\misc work\xmledit\SalesTransactionCustom.xml')

并得到一个xml文件,如:

<?xml version="1.0" encoding="UTF-8"?>
<ProcessSalesTransactionCustom xmlns="http://schema.xxxx.com/xxxxx/2" releaseID="9.2">
  <ApplicationArea>
    <Sender>
      <LogicalID>xxxxxx.file.syncxxxxx5salesinvoice</LogicalID>
      <ComponentID>External</ComponentID>
      <ConfirmationCode>OnError</ConfirmationCode>
    </Sender>
    <CreationDateTime>2020-04-16T14:50:26.976Z</CreationDateTime>
    <BODID>xxxx-nid:xxxxx:1001::Default_1001#320000:?SalesTransactionCustom&amp;verb=Process</BODID>
  </ApplicationArea>
  <DataArea>
    <Process>
      <TenantID>xxx</TenantID>
      <AccountingEntityID>4710</AccountingEntityID>
      <LocationID>S_4710</LocationID>
      <ActionCriteria>
        <ActionExpression actionCode="Add"/>
      </ActionCriteria>
    </Process>
    <SalesTransactionCustom>
      <FinancialBatch>
        <TransactionDate>2019-09-27T00:00:00</TransactionDate>
        <BatchReference>KUKS_20190928052427</BatchReference>
      </FinancialBatch>
      <TransactionHeader>
        <TransactionType>HEI</TransactionType>
        <SalesInvoice>
          <Invoice>19001160</Invoice>
          <BusinessPartner>417B00</BusinessPartner>
          <DocumentDate>2019-09-27T00:00:00</DocumentDate>
          <DueDate>2019-11-20T00:00:00</DueDate>
          <Amount>152248.80</Amount>
          <Currency>EUR</Currency>
          <TaxCountry>DK</TaxCountry>
          <TaxCode>BESIT</TaxCode>
          <NonFinalizedTransaction>
            <TransactionReference>417B00 PC210LCI-11</TransactionReference>
            <LedgerAccount>50000400</LedgerAccount>
            <Dimension1>100</Dimension1>
            <Dimension2>KUK</Dimension2>
            <Dimension3/>
            <Dimension4/>
            <Dimension5/>
            <Dimension6/>
            <Dimension7/>
            <Dimension8/>
            <TaxAmount>0.00</TaxAmount>
            <DebitCreditFlag>credit</DebitCreditFlag>
            <Amount>152248.80</Amount>
          </NonFinalizedTransaction>
        </SalesInvoice>
      </TransactionHeader>
      <TransactionHeader>
        <TransactionType>HEI</TransactionType>
        <SalesInvoice>
          <Invoice>19001161</Invoice>
          <BusinessPartner>412600</BusinessPartner>
          <DocumentDate>2019-09-27T00:00:00</DocumentDate>
          <DueDate>2019-11-20T00:00:00</DueDate>
          <Amount>113848.17</Amount>
          <Currency>EUR</Currency>
          <TaxCountry>AT</TaxCountry>
          <TaxCode>GBSI</TaxCode>
          <NonFinalizedTransaction>
            <TransactionReference>412600 PC210NLC-11</TransactionReference>
            <LedgerAccount>50000400</LedgerAccount>
            <Dimension1>100</Dimension1>
            <Dimension2>KUK</Dimension2>
            <Dimension3/>
            <Dimension4/>
            <Dimension5/>
            <Dimension6/>
            <Dimension7/>
            <Dimension8/>
            <TaxAmount>0.00</TaxAmount>
            <DebitCreditFlag>credit</DebitCreditFlag>
            <Amount>113848.17</Amount>
          </NonFinalizedTransaction>
        </SalesInvoice>
      </TransactionHeader>
    </SalesTransactionCustom>
  </DataArea>
</ProcessSalesTransactionCustom>

我有一个类似的熊猫数据框(这里第一行是列名):

Tag             Old Value   New Value
BusinessPartner 417B00      BPE000104
BusinessPartner 412600      BPE000153
LedgerAccount   50000400    108092200

我想参考这个 pandas 数据框来替换 xml 中元素的属性。我希望能够找到标签和旧值的组合,并用新值替换属性。我还需要能够将编辑后的文本作为 XML 写回磁盘。

如何使用 lxml 和 pandas 做到这一点?

提前谢谢你

编辑:感谢@Partha Mandal,这是有效的代码

import pandas as pd
from lxml import etree

df=pd.read_excel("Sample.xlsx")
df.columns=['Tag','Old','New']
df['Old'] = df['Old'].astype(str)
df['New'] = df['New'].astype(str)

parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(r'C:\Users\xxx\Desktop\misc work\xmledit\testxml2.xml',parser)
string = etree.tostring(tree)
string = bytes.decode(string)

tag = df.Tag; old = df.Old; new = df.New

for i in range(len(tag)):
   string = string.replace("<"+tag[i]+">"+old[i]+"</"+tag[i]+">","<"+tag[i]+">"+new[i]+"</"+tag[i]+">")

string=str.encode(string)

root = etree.fromstring(string)
my_tree = etree.ElementTree(root)
with open('testxml2.xml', 'wb') as f:
    f.write(etree.tostring(my_tree))

【问题讨论】:

    标签: xml pandas lxml


    【解决方案1】:

    为什么不直接将XML 读取为字符串并执行str.replace

    tag = df.Tag; old = df.Old; new = df.New
    
    for i in range(len(tag)):
       _str = _str.replace("<"+tag[i]+">"+old[i]+"</"+tag[i]+">","<"+tag[i]+">"+new[i]+"</"+tag[i]+">")
    

    【讨论】:

    • 我添加了这段代码以将其读取为字符串 - 'string = etree.tostring(tree)'。然后我在您的评论中运行了 for 循环,但没有下划线(因为我不确定那是什么,也没有定义这样的变量)。所以我用'string'而不是'_str'运行for循环,但得到以下错误:TypeError: a bytes-like object is required, not 'str'
    • 你能在运行str.replace之前运行string = bytes.decode(string)吗?
    • 谢谢。我按照你说的做了,现在已经编辑了字符串。但是,我还需要将它作为 XML 写回磁盘。 (我已经编辑了问题)我正在尝试代码 - with open('sample2.xml', 'wb') as f: string.write(f, encoding="utf-8", xml_declaration=True, pretty_print=True) 但得到错误“字节”对象没有属性“写入”。 (我再次尝试解码为字符串,但使用 'str' 而不是 'bytes' 得到相同的错误)
    • 哦!你不应该使用f.write...,这可能是那里的问题吗?
    【解决方案2】:

    因为您使用lxml,请考虑XSLT,这是一种专用语言,旨在将XML 文件转换为不同的XML,并支持从顶层(例如Python)传递参数。因此,将参数化集成到数据帧记录的循环中:

    唯一的挑战是将 Tag 的所有唯一值硬编码到 XSLT 的第二个模板匹配中(管道后换行很好):

    doc:BusinessPartner|doc:LedgerAccount
    

    你可以做什么

    "|".join(['doc:'+ val for val in df['Tag'].unique()])
    
    "|\n".join(['doc:'+ val for val in df['Tag'].unique()])
    

    XSLT (另存为.xsl,一个特殊的.xml文件)

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                                  xmlns:doc="http://schema.xxxx.com/xxxxx/2">
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
    
      <!-- PARAMETERS -->
      <xsl:param name="tag" />
      <xsl:param name="old_value" />
      <xsl:param name="new_value" />
    
      <!-- IDENTITY TRANSFORM -->
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    
      <!-- CONDITIONALL ASSIGN PARAMS --> 
      <xsl:template match="doc:BusinessPartner|doc:LedgerAccount">
        <xsl:choose>
            <xsl:when test = "text() = $old_value">
                <xsl:copy>
                    <xsl:value-of select="$new_value"/>
                </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:value-of select="text()"/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
      </xsl:template>
    
    </xsl:stylesheet>
    

    Python

    import pandas as pd
    import lxml.etree as et
    
    df = pd.read_csv(...)
    
    # LOAD XML AND XSL SCRIPT
    xml = et.parse('Input.xml')
    xsl = et.parse('Script.xsl')
    transform = et.XSLT(xsl)
    
    # PASS PARAMETER TO XSLT
    df_list = df.to_dict('records')
    
    for v in df_list:   
        result = transform(xml, tag = et.XSLT.strparam(v['Tag']), 
                                old_value = et.XSLT.strparam(v['Old Value']), 
                                new_value = et.XSLT.strparam(v['New Value']))
    
        xml = result
    
    # SAVE TO NEW XML
    with open("Output.xml", 'wb') as f:
        f.write(result)
    

    XML输出

    <?xml version="1.0"?>
    <ProcessSalesTransactionCustom xmlns="http://schema.xxxx.com/xxxxx/2" releaseID="9.2">
      <ApplicationArea>
        <Sender>
          <LogicalID>xxxxxx.file.syncxxxxx5salesinvoice</LogicalID>
          <ComponentID>External</ComponentID>
          <ConfirmationCode>OnError</ConfirmationCode>
        </Sender>
        <CreationDateTime>2020-04-16T14:50:26.976Z</CreationDateTime>
        <BODID>xxxx-nid:xxxxx:1001::Default_1001#320000:?SalesTransactionCustom&amp;verb=Process</BODID>
      </ApplicationArea>
      <DataArea>
        <Process>
          <TenantID>infor</TenantID>
          <AccountingEntityID>4710</AccountingEntityID>
          <LocationID>S_4710</LocationID>
          <ActionCriteria>
            <ActionExpression actionCode="Add"/>
          </ActionCriteria>
        </Process>
        <SalesTransactionCustom>
          <FinancialBatch>
            <TransactionDate>2019-09-27T00:00:00</TransactionDate>
            <BatchReference>KUKS_20190928052427</BatchReference>
          </FinancialBatch>
          <TransactionHeader>
            <TransactionType>HEI</TransactionType>
            <SalesInvoice>
              <Invoice>19001160</Invoice>
              <BusinessPartner>BPE000104</BusinessPartner>
              <DocumentDate>2019-09-27T00:00:00</DocumentDate>
              <DueDate>2019-11-20T00:00:00</DueDate>
              <Amount>152248.80</Amount>
              <Currency>EUR</Currency>
              <TaxCountry>DK</TaxCountry>
              <TaxCode>BESIT</TaxCode>
              <NonFinalizedTransaction>
                <TransactionReference>417B00 PC210LCI-11</TransactionReference>
                <LedgerAccount>108092200</LedgerAccount>
                <Dimension1>100</Dimension1>
                <Dimension2>KUK</Dimension2>
                <Dimension3/>
                <Dimension4/>
                <Dimension5/>
                <Dimension6/>
                <Dimension7/>
                <Dimension8/>
                <TaxAmount>0.00</TaxAmount>
                <DebitCreditFlag>credit</DebitCreditFlag>
                <Amount>152248.80</Amount>
              </NonFinalizedTransaction>
            </SalesInvoice>
          </TransactionHeader>
          <TransactionHeader>
            <TransactionType>HEI</TransactionType>
            <SalesInvoice>
              <Invoice>19001161</Invoice>
              <BusinessPartner>BPE000153</BusinessPartner>
              <DocumentDate>2019-09-27T00:00:00</DocumentDate>
              <DueDate>2019-11-20T00:00:00</DueDate>
              <Amount>113848.17</Amount>
              <Currency>EUR</Currency>
              <TaxCountry>AT</TaxCountry>
              <TaxCode>GBSI</TaxCode>
              <NonFinalizedTransaction>
                <TransactionReference>412600 PC210NLC-11</TransactionReference>
                <LedgerAccount>108092200</LedgerAccount>
                <Dimension1>100</Dimension1>
                <Dimension2>KUK</Dimension2>
                <Dimension3/>
                <Dimension4/>
                <Dimension5/>
                <Dimension6/>
                <Dimension7/>
                <Dimension8/>
                <TaxAmount>0.00</TaxAmount>
                <DebitCreditFlag>credit</DebitCreditFlag>
                <Amount>113848.17</Amount>
              </NonFinalizedTransaction>
            </SalesInvoice>
          </TransactionHeader>
        </SalesTransactionCustom>
      </DataArea>
    </ProcessSalesTransactionCustom>
    

    【讨论】:

      猜你喜欢
      • 2018-02-23
      • 1970-01-01
      • 2013-12-23
      • 1970-01-01
      • 1970-01-01
      • 2012-08-26
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      相关资源
      最近更新 更多