Python 使用Lxml创建动态XML元素

Python 使用Lxml创建动态XML元素,python,xml,loops,nested,lxml,Python,Xml,Loops,Nested,Lxml,我在使用lxml库生成XML方面得到了一些帮助,这非常有用,我能够扩展它来解决我的大多数问题。有一个用例我正在努力解决。我试过一个建议,但仍在努力 下面是我的数据集的简单表示 ID,Currency,Notional,Maturity,Type ID1,,,,2018-06-01, ID1-L1,EUR,100,,,Bond ID1-L2,JPY,110,,A ID1-L2,CNY,115,,B ID2,,,,2018-06-01, ID2-L1,EUR,100,,,Stock ID2-L2,J

我在使用lxml库生成XML方面得到了一些帮助,这非常有用,我能够扩展它来解决我的大多数问题。有一个用例我正在努力解决。我试过一个建议,但仍在努力

下面是我的数据集的简单表示

ID,Currency,Notional,Maturity,Type
ID1,,,,2018-06-01,
ID1-L1,EUR,100,,,Bond
ID1-L2,JPY,110,,A
ID1-L2,CNY,115,,B
ID2,,,,2018-06-01,
ID2-L1,EUR,100,,,Stock
ID2-L2,JPY,110,,C
ID2-L2,JPY,110,,D
基本上我这里有两个记录ID-1和ID2。ID-L1、ID-L2等是ID1的子元素,将有多个ID-L2实例。我的问题是我需要识别ID-L2等的所有事件,并为每个事件创建一个新元素,然后移动到下一条记录ID2并重复。所以实际上我的结果是这样的

<tradeRequests>
    <ids>
    <mainid>ID1</mainid>
            <element>
                <maturityDate>2018-06-01</maturityDate>
            </element>
                <cffixed>
                    <element>
                        <id>ID-L1</id>
                        <currency>EUR</currency>
                    </element>
                </cffixed>
                <cffloat>
                    <element>
                        <id>ID1-L2</id>
                        <currency>JPY</currency>
                    </element>
                    <element>
                        <id>ID1-L2</id>
                        <currency>CNY</currency>
                    </element>
                </cffloat>
        </ids>
</tradeRequests>

我的问题是,我可以独立地找到一种识别ID-L2行的方法,但不确定如何让for循环使用它。这确实是缺少的拼图,因此我们一如既往地感谢您的帮助。

此方法使用
itertools.groupby
在生成元素之前按ID对数据进行分组。这样就可以为每个元素添加一个
mainid

import itertools
import csv
import lxml.etree
from lxml.builder import E

with open('tc.csv', 'r') as fb:
    cf = csv.DictReader(fb)
    def groupkey(row):
        return row['ID'].split('-')[0] # group by first part of ID

    result_ids = E.ids()
    result = E.tradeRequests(result_ids)

    for main_id, rows in itertools.groupby(cf, key=groupkey):
        rows = list(rows)
        result_ids.extend([
            E.mainid(main_id),
            E.element(E.maturityDate(rows[0]['Type'])),
            E.cffixed(E.element(E.id(rows[1]['ID']), E.currency(rows[1]['Currency']))),
            E.cffloat(*(E.element(E.id(r['ID']), E.currency(r['Currency']))
                for r in rows[2:])),
        ])
print(lxml.etree.tostring(result, pretty_print=True))
使用问题中提供的csv运行时,结果如下:

<tradeRequests>
  <ids>
    <mainid>ID1</mainid>
    <element>
      <maturityDate>2018-06-01</maturityDate>
    </element>
    <cffixed>
      <element>
        <id>ID1-L1</id>
        <currency>EUR</currency>
      </element>
    </cffixed>
    <cffloat>
      <element>
        <id>ID1-L2</id>
        <currency>JPY</currency>
      </element>
      <element>
        <id>ID1-L2</id>
        <currency>CNY</currency>
      </element>
    </cffloat>
    <mainid>ID2</mainid>
    <element>
      <maturityDate>2018-06-01</maturityDate>
    </element>
    <cffixed>
      <element>
        <id>ID2-L1</id>
        <currency>EUR</currency>
      </element>
    </cffixed>
    <cffloat>
      <element>
        <id>ID2-L2</id>
        <currency>JPY</currency>
      </element>
      <element>
        <id>ID2-L2</id>
        <currency>JPY</currency>
      </element>
    </cffloat>
  </ids>
</tradeRequests>

ID1
2018-06-01
ID1-L1
欧元
ID1-L2
日元
ID1-L2
人民币
ID2
2018-06-01
ID2-L1
欧元
ID2-L2
日元
ID2-L2
日元

使用
etree.XML
etree.fromstring
构建基础XML。使用xpath找到要添加下一个元素的位置。感谢@shrewmouse的建议。最后,nosklo下面的方法解决了这个问题。这是很棒的@nosklo,看起来不错。我永远不会解决这个问题。谢谢你救了我的命。
<tradeRequests>
  <ids>
    <mainid>ID1</mainid>
    <element>
      <maturityDate>2018-06-01</maturityDate>
    </element>
    <cffixed>
      <element>
        <id>ID1-L1</id>
        <currency>EUR</currency>
      </element>
    </cffixed>
    <cffloat>
      <element>
        <id>ID1-L2</id>
        <currency>JPY</currency>
      </element>
      <element>
        <id>ID1-L2</id>
        <currency>CNY</currency>
      </element>
    </cffloat>
    <mainid>ID2</mainid>
    <element>
      <maturityDate>2018-06-01</maturityDate>
    </element>
    <cffixed>
      <element>
        <id>ID2-L1</id>
        <currency>EUR</currency>
      </element>
    </cffixed>
    <cffloat>
      <element>
        <id>ID2-L2</id>
        <currency>JPY</currency>
      </element>
      <element>
        <id>ID2-L2</id>
        <currency>JPY</currency>
      </element>
    </cffloat>
  </ids>
</tradeRequests>