简体   繁体   中英

Parsing Dictionary in python

I have this XML which upon parsing gives dictionary with parent child relationship

 import xml.etree.ElementTree as ET

def remove_value(listOfDicts, key):
    for subVal in listOfDicts:
        if key in subVal:
            del subVal[key]

def get_children(parent):
    for child in parent:
        if "ACTION" in child.tag:
            continue
        if 'BOOLOP' in child.attrib:
            yield child.attrib['BOOLOP']
        else:
            yield child.attrib


def get_parent_children_mapping(tree):
    return { parent: list(get_children(parent)) for parent in tree.iter() if "ACTION" not in parent.tag }

s = """
  <RULE COMMENT="" DEFAULTPRIORITY="50" DESCRIPTION="DefaultShipping" DISABLEFLG="N" DefinedOutsideTree="true" FIRINGPHASE="" NAME="DefaultShipping" PATH="PB.SMB.DefaultShipping" RULECLASSIFICATION="PICK" TRIGGER="1">
            <BOOLOP BOOLOP="and" SEQ="0" TYPE="0">
                <BOOLOP BOOLOP="or" SEQ="1" TYPE="0">
                    <FRAGMENT FUNC1="value" FUNC2="propval" NULLACTION="Fragment is false" OP="=" PROP1="DEFAULT_SKU1" PROP2="_sku" SEQ="1" TYPE="1"/>
                    <FRAGMENT FUNC1="value" FUNC2="propval" NULLACTION="Fragment is false" OP="=" PROP1="DEFAULT_SKU2" PROP2="_sku" SEQ="2" TYPE="1"/>
                    <FRAGMENT FUNC1="value" FUNC2="propval" NULLACTION="Fragment is false" OP="=" PROP1="DEFAULT_SKU3" PROP2="_sku" SEQ="3" TYPE="1"/>
                </BOOLOP>
                <FRAGMENT FUNC1="propval" FUNC2="literal" NULLACTION="Fragment is false" OP="=" PROP1="_amEntitled" PROP2="1" SEQ="2" TYPE="1"/>
                <BOOLOP BOOLOP="and" SEQ="3" TYPE="0">
                    <FRAGMENT FUNC1="value" FUNC2="literal" NULLACTION="Fragment is true" OP="!=" PROP1="IsShipingSelected" PROP2="yes" SEQ="1" TYPE="1"/>
                    <FRAGMENT FUNC1="value" FUNC2="literal" NULLACTION="Fragment is true" OP="!=" PROP1="IsShipingSelected" PROP2="no" SEQ="2" TYPE="1"/>
                </BOOLOP>
            </BOOLOP>
            </RULE>
"""
entries = ['NULLACTION','SEQ','TYPE']
tree = ET.fromstring(s)
for parent, children in get_parent_children_mapping(tree).items():
    if children:
        for vals in entries:
            remove_value(children, vals)
        if 'NAME' in parent.attrib:
            print("{0} : {1}".format(parent.attrib['NAME'], children))


        elif 'BOOLOP' in parent.attrib:
            print("{0} : {1}".format(parent.attrib['BOOLOP'], children))

        else:
            print("{0} : {1}".format(parent.tag, children))

The program prints a output like this upon iterating over the items:

OUTPUT :

DefaultShipping : ['and']
    and : ['or', {'FUNC1': 'propval', 'FUNC2': 'literal', 'OP': '=', 'PROP1': '_amEntitled', 'PROP2': '1'}, 'and']
    or : [{'FUNC1': 'value', 'FUNC2': 'propval', 'OP': '=', 'PROP1': 'DEFAULT_SKU1', 'PROP2': '_sku'}, {'FUNC1': 'value', 'FUNC2': 'propval', 'OP': '=', 'PROP1': 'DEFAULT_SKU2', 'PROP2': '_sku'}, {'FUNC1': 'value', 'FUNC2': 'propval', 'OP': '=', 'PROP1': 'DEFAULT_SKU3', 'PROP2': '_sku'}]
    and : [{'FUNC1': 'value', 'FUNC2': 'literal', 'OP': '!=', 'PROP1': 'IsShipingSelected', 'PROP2': 'yes'}, {'FUNC1': 'value', 'FUNC2': 'literal', 'OP': '!=', 'PROP1': 'IsShipingSelected', 'PROP2': 'no'}]

The output depicts the parent child relationship. Now I want to further manipulate the output as something like this:

(FUNC1(PROP1) OP FUNC2(PROP2) and FUNC1(PROP1) OP FUNC2(PROP2)) or (FUNC1(PROP1) OP FUNC2(PROP2))

Is there something inbuilt in parser or do I need to write my own parser. Any help is appreciated.

NOTE

In my output example by FUNC1, FUNC2,PROP1 etc. I mean their values.

Thanks to @ Martijn Pieters for guiding about the Syntax trees.

No, there is no in-built parser, nor do you need a parser. You need to build a syntax tree , which is not that hard:

from dataclasses import dataclass, field, fields
from typing import List

@dataclass
class Node:
    seq: int
    type: int

    @classmethod
    def tree_from_xml(cls, node):
        name = node.tag
        for cls in cls.__subclasses__():
            if cls.__name__.upper() == name:
                return cls.node_from_xml(node)

    @classmethod
    def node_from_xml(cls, node, **kwargs):
        fieldargs = {f.name: node.attrib.get(f.name.upper()) for f in fields(cls)}
        fieldargs['seq'] = int(fieldargs['seq'])
        fieldargs['type'] = int(fieldargs['type'])
        fieldargs.update(kwargs)
        return cls(**fieldargs)

@dataclass
class BoolOp(Node):
    boolop: str
    operands: List[Node] = field(default_factory=list)

    def __str__(self):
        joined = f' {self.boolop} '.join(map(str, self.operands))
        return f'({joined})'

    @classmethod
    def node_from_xml(cls, node):
        operands = (Node.tree_from_xml(child) for child in node)
        return super().node_from_xml(node, operands=[op for op in operands if op])

@dataclass
class Fragment(Node):
    func1: str
    func2: str
    nullaction: str
    op: str
    prop1: str
    prop2: str

    def __str__(self):
        return f'{self.func1}({self.prop1}) {self.op} {self.func2}({self.prop2})'

then build those objects from the top-level BOOLOP XML node, and print the result:

rule = Node.tree_from_xml(tree.find('.//BOOLOP'))
print(rule)

Note that rule is a Node subclass, so you can still inspect the tree directly, or call str() on that object to get the string conversion.

Demo:

>>> rule = Node.tree_from_xml(tree.find('.//BOOLOP'))
>>> print(rule)
((value(DEFAULT_SKU1) = propval(_sku) or value(DEFAULT_SKU2) = propval(_sku) or value(DEFAULT_SKU3) = propval(_sku)) and propval(_amEntitled) = literal(1) and (value(IsShipingSelected) != literal(yes) and value(IsShipingSelected) != literal(no)))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM