Я разбираю тысячи XML-файлов в словари и сохраняю их структуры в JSON.
У них почти такая же структура, но количество различных схем именования тегов неизвестно. Существует множество различных сокращений для именования тегов в этих тысячах файлов.
Мне нужно выяснить, сколько разных тегов существует для описания каждой части информации, чтобы правильно их проанализировать.
Для этого я хочу создать один главный словарь XML / словарей, который включает все варианты имен тегов и, желательно, их количество в тысячах XML / словарей.
Вот небольшой образец одного из словарей:
{
"Header": {
"Ts": {},
"PeriodEndDt": {},
"PreparedBy": {
"PreparerID": {},
"PreparerFirmName": {
"BusinessNameLine1Txt": {}
},
"PreparerAddress": {
"AddLn1Txt": {},
"CityName": {},
"StateAbbreviationCd": {},
"ZIPCd": {}
}
},
"FormTypeCd": {},
"PeriodBeginDt": {},
"Filer": {
"UniqueID": {},
"BusinessName": {
"BusinessNameLine1Txt": {}
},
"BusinessNameControlTxt": {},
"PhoneNum": {},
"USAddress": {
"AddressLine1Txt": {},
"CityNm": {},
"StateAbbreviationCd": {},
"ZIPCd": {}
}
},
"FormData": {
"FormCodeType": {
"BizType": {},
"AssetsAtEOY": {},
"AccountingMethod": {},
"RevenueAndExpenses": {
"ScheduleBNotReqd": {},
"DivsRevAndExpenses": {},
"DivsNetInvstIncomeAmt": {},
"NetGainSaleAstRevAndExpnssAmt": {},
"RevsOvrExpenses": {},
"NetInvestmentIncomeAmt": {}
},
"BalanceSheetGroup": {
"CashInvstBOYAmt": {},
"CashInvstEOYAmt": {},
"CashInvstEOYFMVAmt": {},
"OtherInvestmentsBOYAmt": {},
"OtherInvestmentsEOYAmt": {},
"CapitalStockEOYAmt": {},
"TotalLiabilitiesNetAstEOYAmt": {}
},
"ChangeNetAssetsFundGroup": {
"NetAssettFundBalancesBOYAmt": {},
"ExcessRevExpensesAmt": {},
"OtherIncreasesAmt": {},
"SubtotalAmt": {},
"OtherDecreasesAmt": {},
"TotNetAstOrFundBalancesEOYAmt": {}
},
"CapGainsLossTxInvstIncmDetail": {
"CapGainsLossTxInvstIncmGrp": {
"PropertyDesc": {},
"HowAcquiredCd": {},
"GrossSalesPriceAmt": {},
"GainOrLossAmt": {},
"GainsMinusExcessOrLossesAmt": {}
},
"StatementsRegardingActyGrp": {
"LegislativePoliticalActyInd": {},
"MoreThan100SpentInd": {}
},
"PhoneNum": {},
"LocationOfBooksUSAddress": {
"AddressLine1Txt": {},
"CityNm": {},
"StateAbbreviationCd": {},
"ZIPCd": {}
},
"CorporateDirectorsGrp": {
"DirectorsGrp": {
"PersonNm": {},
"USAddress": {
"AddressLine1Txt": {},
"CityNm": {},
"StateAbbreviationCd": {},
"ZIPCd": {}
},
"EmpPrograms": {
"EmployeeBenefitGroupNum": {},
"GroupType": {
"GroupElement": {},
"GroupCharacter": {
"GroupNames": {}
}
}
},
"EmpOffice1": {},
"EmpOffice2": {},
"EmpOffice3": {},
"EmpOffice4": {}
}
}
}
}
}
}
}
Код, который я использую для создания словарей / JSON в первую очередь, выглядит следующим образом:
import xml.etree.ElementTree as ET
strip_ns = lambda xx: str(xx).split('}', 1)[1]
tree = ET.parse('xmlpath.xml')
root = tree.getroot()
tierdict = {}
for tier1 in root:
tier1var = strip_ns(tier1.tag)
tierdict[tier1var] = {}
for tier2 in tier1:
tier2var = strip_ns(tier2.tag)
tierdict[tier1var][tier2var] = {}
for tier3 in tier2:
tier3var = strip_ns(tier3.tag)
tierdict[tier1var][tier2var][tier3var] = {}
for tier4 in tier3:
tier4var = strip_ns(tier4.tag)
tierdict[tier1var][tier2var][tier3var][tier4var] = {}
Результат, который я хотел бы увидеть, выглядит примерно так:
{
"Header": {
"Header.Count": 5672,
"Ts": {
"Ts.Count": 3365
},
"Ss": {
"Ss.Count": 2328
},






Я бы, вероятно, выполнил рекурсивный поиск нужных вам элементов, как определено ниже:
def get_elements(json_entry, child_elements=[]):
if not child_elements:
return json_entry
el, other_children = child_elements[0], child_elements[1:]
children = el.getchildren()
rec = json_entry.get(el.tag)
if not children:
json_entry[el.tag] = {"Count": rec.get("Count",0)+1 if rec else 1}
else:
json_entry[el.tag] = {"Count": rec.get("Count",0) if rec else 1,
**get_elements({}, children)}
return get_elements(json_entry, other_children)
Таким образом, вы можете просто передать корневой элемент вашего xml:
from lxml import etree
with open("myxml.xml", "r") as fh:
tree = etree.parse(fh)
root = tree.getroot()
root_children = root.getchildren()
child_recs = get_elements({}, root_children)
{'tagOne': {'Count': 1}, 'tagTwo': {'Count': 1, 'tagThree': {'Count': 1}, 'tagFour': {'Count': 1, 'tagFive': {'Count': 1}}}}
Если вы хотите обернуть вокруг него корневой элемент, сделайте это так:
master_lookup = {root.tag: {"Count": 1, **child_recs}}
Это может быть легко расширено до цикла for через множество файлов.
master_lookup = {}
for file in os.walk(path):
with open(file) as fh:
tree = etree.parse(fh)
root = tree.getroot()
root_entry = master_lookup.get(root.tag, {"Count": 0})
root_children = root.getchildren()
root_count = root_entry.pop("Count")
master_lookup[root.tag] = {"Count": root_count, **get_elements({**root_entry}, root_children)}
Что-то в этом роде
getchildren является функцией Element (часть etree). Таким образом, каждый Element в дереве имеет этот метод. Он вернет список всех дочерних элементов (пустой список, если его нет), поэтому распаковка не выдает IndexErrorЯ запускаю это в Python 3.6, если это важно
Еще раз спасибо за вашу помощь. Я все еще пытаюсь заставить это работать. На данный момент я, должно быть, делаю что-то не так с циклом, так что окончательное значение master_lookup, кажется, получено только из одного файла со всеми counts = 1 и кажущимся количеством отдельных полей из различных файлов, не включенных в этот диктат.
Так что для цикла файлов это не на 100% правильно. Я сделаю правку
Большое вам спасибо за все это! Я все еще не совсем там. Некоторые области, над которыми я работаю: при первом прохождении цикла, после первого определения root_entry, его значение равно {'Count': 0}. При последующих запусках он, кажется, принимает в качестве значения весь набор тегов из XML-файла, но затем начинает с того же самого {'Count': 0} в качестве своей первой пары kv. С другой стороны, значение root_children в основном цикле кажется именно тем, что я ожидал каждый раз: пара тегов, вложенных непосредственно под корень. И в целом вывод master_lookup такой же, как и раньше.
Проделав немного больше работы над этим, get_elements, похоже, делает работу по созданию словаря для любого 1 файла с правильным вложением и счетчиком 1. Теперь вопрос заключается в том, как сделать внешний цикл, вызываемый master_lookup, счетчиком с сравнением / кроме того, поскольку он просто заполняет dict последним запущенным файлом.
Извините за всю головную боль, рекурсия - это определенно то, что вам нужно. Цикл должен захватывать root.tag из текущего dict. Если он есть, он pops подсчитывает, таким образом, он может просто выгружать остальное в функцию рекурсии. Похоже, что для второго rec.get("Count", 0) нужен +1, в противном случае, как вы заявили, нет никакого дополнения.
Спасибо за подробный ответ! Откуда вы берете
getchildren()? Везде, где я это видел, это не часть Python 3.