How to return the full hierarchy of an XML node?

43 Views Asked by At

Given the following XML snippet:

<Profile>
    <Settings>
        <PresentationParameters>
            <Annualize>True</Annualize>
            <LoadExAnteRiskForPresentation>False</LoadExAnteRiskForPresentation>
            <MultiLegDisplayMode>Legs</MultiLegDisplayMode>
            <Parameters>
                <InitialDisplayMode>CollapseToPositions</InitialDisplayMode>
                <InitialExpandCollapseLevel>-1</InitialExpandCollapseLevel>
            </Parameters>
            <PivotExpandCollapseStrategy>ExpandAllStrategy</PivotExpandCollapseStrategy>
            <RollupExternallyManagedSleeves>False</RollupExternallyManagedSleeves>
            <SharedParameters>
                <AssetBasisInEffect>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisInEffect>
                <AssetBasisSaveable>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisSaveable>
                <CurrencySaveable>
                    <Mode>GROUP</Mode>
                </CurrencySaveable>
                <IsSessionBasisInEffect>True</IsSessionBasisInEffect>
            </SharedParameters>
            <ShowPositionsGrandTotal>True</ShowPositionsGrandTotal>
            <TargetingCurrency>
                <Mode>GROUP</Mode>
            </TargetingCurrency>
            <TransposeColumns>False</TransposeColumns>
            <UnitBasisMeasureOption>PercentagePoints</UnitBasisMeasureOption>
            <UnitTrustConstituentMode>DoNotDisplay</UnitTrustConstituentMode>
            <WeightProrationOption>Default</WeightProrationOption>
        </PresentationParameters>
    </Settings>
</Profile>

I want to return a result that shows the full path of each leaf node, along with its value - as such:

Settings.PresentationParameters.Annualize  TRUE
Settings.PresentationParameters.LoadExAnteRiskForPresentation  FALSE
Settings.PresentationParameters.MultiLegDisplayMode Legs
Settings.PresentationParameters.Parameters.InitialDisplayMode  CollapseToPositions
...

Looking for advice on how to retrieve the entire path of the node.

3

There are 3 best solutions below

0
AndJ On BEST ANSWER

I hope I've understood the question correctly. A possible solution in python could be this:

import xml.etree.ElementTree as ET

def dfs(node, path):
    if len(node) == 0:  # If the node is a leaf
        print(f'{path}.{node.tag} {node.text}')
    else:
        for child in node:
            dfs(child, f'{path}.{node.tag}')

xml_string = """<Profile>
    <Settings>
        <PresentationParameters>
            <Annualize>True</Annualize>
            <LoadExAnteRiskForPresentation>False</LoadExAnteRiskForPresentation>
            <MultiLegDisplayMode>Legs</MultiLegDisplayMode>
            <Parameters>
                <InitialDisplayMode>CollapseToPositions</InitialDisplayMode>
                <InitialExpandCollapseLevel>-1</InitialExpandCollapseLevel>
            </Parameters>
            <PivotExpandCollapseStrategy>ExpandAllStrategy</PivotExpandCollapseStrategy>
            <RollupExternallyManagedSleeves>False</RollupExternallyManagedSleeves>
            <SharedParameters>
                <AssetBasisInEffect>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisInEffect>
                <AssetBasisSaveable>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisSaveable>
                <CurrencySaveable>
                    <Mode>GROUP</Mode>
                </CurrencySaveable>
                <IsSessionBasisInEffect>True</IsSessionBasisInEffect>
            </SharedParameters>
            <ShowPositionsGrandTotal>True</ShowPositionsGrandTotal>
            <TargetingCurrency>
                <Mode>GROUP</Mode>
            </TargetingCurrency>
            <TransposeColumns>False</TransposeColumns>
            <UnitBasisMeasureOption>PercentagePoints</UnitBasisMeasureOption>
            <UnitTrustConstituentMode>DoNotDisplay</UnitTrustConstituentMode>
            <WeightProrationOption>Default</WeightProrationOption>
        </PresentationParameters>
    </Settings>
</Profile>
"""
root = ET.fromstring(xml_string)
dfs(root, '')

It returns the following output:

.Profile.Settings.PresentationParameters.Annualize True
.Profile.Settings.PresentationParameters.LoadExAnteRiskForPresentation False
.Profile.Settings.PresentationParameters.MultiLegDisplayMode Legs
.Profile.Settings.PresentationParameters.Parameters.InitialDisplayMode CollapseToPositions
.Profile.Settings.PresentationParameters.Parameters.InitialExpandCollapseLevel -1
.Profile.Settings.PresentationParameters.PivotExpandCollapseStrategy ExpandAllStrategy
.Profile.Settings.PresentationParameters.RollupExternallyManagedSleeves False
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisInEffect.CalculationType Invalid
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisInEffect.IsColumnBasisInvested False
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisInEffect.Type Standard
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisInEffect.Value MWB_SESSION
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisSaveable.CalculationType Invalid
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisSaveable.IsColumnBasisInvested False
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisSaveable.Type Standard
.Profile.Settings.PresentationParameters.SharedParameters.AssetBasisSaveable.Value MWB_SESSION
.Profile.Settings.PresentationParameters.SharedParameters.CurrencySaveable.Mode GROUP
.Profile.Settings.PresentationParameters.SharedParameters.IsSessionBasisInEffect True
.Profile.Settings.PresentationParameters.ShowPositionsGrandTotal True
.Profile.Settings.PresentationParameters.TargetingCurrency.Mode GROUP
.Profile.Settings.PresentationParameters.TransposeColumns False
.Profile.Settings.PresentationParameters.UnitBasisMeasureOption PercentagePoints
.Profile.Settings.PresentationParameters.UnitTrustConstituentMode DoNotDisplay
.Profile.Settings.PresentationParameters.WeightProrationOption Default
0
Michael Kay On

Since you mentioned XPath, the following XPath 3.1 expression does the job:

//*[not(*)] ! 
  ((ancestor-or-self::*/local-name() => string-join('.')) 
    || ' ' || string(.))
0
Hermann12 On

You can get similar result with lxml, getelementpath():

import lxml.etree as etree

xml_str = """<Profile>
    <Settings>
        <PresentationParameters>
            <Annualize>True</Annualize>
            <LoadExAnteRiskForPresentation>False</LoadExAnteRiskForPresentation>
            <MultiLegDisplayMode>Legs</MultiLegDisplayMode>
            <Parameters>
                <InitialDisplayMode>CollapseToPositions</InitialDisplayMode>
                <InitialExpandCollapseLevel>-1</InitialExpandCollapseLevel>
            </Parameters>
            <PivotExpandCollapseStrategy>ExpandAllStrategy</PivotExpandCollapseStrategy>
            <RollupExternallyManagedSleeves>False</RollupExternallyManagedSleeves>
            <SharedParameters>
                <AssetBasisInEffect>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisInEffect>
                <AssetBasisSaveable>
                    <CalculationType>Invalid</CalculationType>
                    <IsColumnBasisInvested>False</IsColumnBasisInvested>
                    <Type>Standard</Type>
                    <Value>MWB_SESSION</Value>
                </AssetBasisSaveable>
                <CurrencySaveable>
                    <Mode>GROUP</Mode>
                </CurrencySaveable>
                <IsSessionBasisInEffect>True</IsSessionBasisInEffect>
            </SharedParameters>
            <ShowPositionsGrandTotal>True</ShowPositionsGrandTotal>
            <TargetingCurrency>
                <Mode>GROUP</Mode>
            </TargetingCurrency>
            <TransposeColumns>False</TransposeColumns>
            <UnitBasisMeasureOption>PercentagePoints</UnitBasisMeasureOption>
            <UnitTrustConstituentMode>DoNotDisplay</UnitTrustConstituentMode>
            <WeightProrationOption>Default</WeightProrationOption>
        </PresentationParameters>
    </Settings>
</Profile>
"""

root = etree.fromstring(xml_str)
tree = etree.ElementTree(root)

for elem in root.iter():
    print(tree.getelementpath(elem), elem.text)