使用 Lxml 创建动态 XML 元素
Create a dynamic XML elements with 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,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>
所以这是我使用过的代码片段,但我对值进行了硬编码,而不是在本示例中引用文件中的内容。
import csv
import lxml.etree
from lxml.builder import E
with open('tc.csv', 'r') as fb:
results = E.tradeRequests(*(
E.ids(
E.mainid('id'),
E.element(
E.MaturityDate('maturity'),
E.cffixed(
E.element(
E.id('id'),
E.currency('currency'),
),
),
E.cffloat(
E.element(
E.id('id'),
E.currency('id'),
) #for r in ids2_rows,
),
),
)for row in csv.DictReader(fb))
)
print(lxml.etree.tostring(results, pretty_print=True))
我的问题是独立的,我可以找到一种方法来识别 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>
我在使用 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>
所以这是我使用过的代码片段,但我对值进行了硬编码,而不是在本示例中引用文件中的内容。
import csv
import lxml.etree
from lxml.builder import E
with open('tc.csv', 'r') as fb:
results = E.tradeRequests(*(
E.ids(
E.mainid('id'),
E.element(
E.MaturityDate('maturity'),
E.cffixed(
E.element(
E.id('id'),
E.currency('currency'),
),
),
E.cffloat(
E.element(
E.id('id'),
E.currency('id'),
) #for r in ids2_rows,
),
),
)for row in csv.DictReader(fb))
)
print(lxml.etree.tostring(results, pretty_print=True))
我的问题是独立的,我可以找到一种方法来识别 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>