Python: сравнивать и считать словарные структуры в тысячах словарей / XML / JSON - PullRequest
0 голосов
/ 18 сентября 2018

Я анализирую многие тысячи файлов 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
            },

1 Ответ

0 голосов
/ 18 сентября 2018

Я бы, вероятно, выполнил рекурсивный поиск нужных вам элементов, как указано ниже:

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)}

Что-то на этот счет

...