To print the data after replacing the given expression with the values in XML file using "ElementTree" library

67 Views Asked by At

Given XML file snippet is:

<?xml version="1.0" standalone="yes"?>
<event_configuration family="21" version="2">
    <pqr subtype="abc">
    <event val="73002" name="$MyCpu"> </event>
    <event val="73003" name="$MyCpuKernel"> </event>

    <metric name="Ratio" expression="$MyCpuKernel / $MyCpu"> </metric>
    </pqr>
</event_configuration>

I have parsed this xml file using "ElementTree" library in Python, please find the code below:

    def parse_xml_to_json(self):
        data = {'metric': []}
        root = ET.fromstring(self.xml_file)

        for element in root.findall('.//*'):
            element_type = element.tag
            if element_type not in ["pqr", "stu", "vwx"]:
                continue
            subtype_name = element.attrib['subtype']
            event_map = {}
            for event in element.findall('.//event'):
                event_name = event.attrib['name']
                val_value = event.attrib['val']
                event_map[event_name] = val_value

            for metric in element.findall('metric'):
                expression = metric.attrib['expression']
                metric_name = metric.attrib['name']
          
                for event_name, val_value in event_map.items():
                    expression = expression.replace(event_name, val_value)

                data['metric'].append({
                    'Name': metric_name,
                    'Expression': expression,
                    'Type': element_type
                })

        return data

I am getting the output, but this code is unable to replace the event name present inside "Expression" with the val_value as shown below:-

Output:

{
"metric": [
    {
        "Name": "Ratio",
        "Expression": "73002Kernel / 73002",
        "Type": "pqr"
    },
    ....
    ....
]
}
    

Here, we can see in the "Expression" it should print "73003 / 73002". I am unable to think of how to solve this issue. Is it possible to use regex here, how to apply it? Please suggest.

2

There are 2 best solutions below

0
Andrej Kesely On BEST ANSWER

Here is an example how you can use re.sub to replace the values in expression:

import re
import xml.etree.ElementTree as ET


def parse_xml_to_json(xml_file):
    data = {"metric": []}
    root = ET.fromstring(xml_file)

    for element in root.findall(".//*"):
        element_type = element.tag
        if element_type not in ["pqr", "stu", "vwx"]:
            continue
        subtype_name = element.attrib["subtype"]
        event_map = {}
        for event in element.findall(".//event"):
            event_name = event.attrib["name"]
            val_value = event.attrib["val"]
            event_map[event_name] = val_value

        for metric in element.findall("metric"):
            expression = metric.attrib["expression"]
            metric_name = metric.attrib["name"]

            for event_name, val_value in event_map.items():
                expression = re.sub(
                    rf"{re.escape(event_name)}\b", val_value, expression
                )

            data["metric"].append(
                {"Name": metric_name, "Expression": expression, "Type": element_type}
            )

    return data


xml_file = """\
<?xml version="1.0" standalone="yes"?>
<event_configuration family="21" version="2">
    <pqr subtype="abc">
    <event val="73002" name="$MyCpu"> </event>
    <event val="73003" name="$MyCpuKernel"> </event>

    <metric name="Ratio" expression="$MyCpuKernel / $MyCpu"> </metric>
    </pqr>
</event_configuration>"""

print(parse_xml_to_json(xml_file))

Prints:

{'metric': [{'Name': 'Ratio', 'Expression': '73003 / 73002', 'Type': 'pqr'}]}
7
Hermann12 On

You can change the XML and create than your JSON. The XML attribute values can be .get() and .set() For the expression attribute you can use an f-string with the values:

Suggested solution in your code:

import xml.etree.ElementTree as ET

class Whatever:
    def __init__(self):
        self.xml_file = "eventConfig.xml"
    
    def parse_xml_to_json(self):
        data = {'metric': []}
        root = ET.parse(self.xml_file) # cahnged parse from file not from string

        for element in root.findall('.//*'):
            element_type = element.tag
            if element_type not in ["pqr", "stu", "vwx"]:
                continue
            subtype_name = element.attrib['subtype']
            event_map = {}
            
            for metric in element.findall('metric'):
                expression = metric.attrib['expression']
                metric_name = metric.attrib['name']

            mycpu = root.find(".//event[@name = '$MyCpu']").get('val')
            mycpukernel = root.find(".//event[@name = '$MyCpuKernel']").get('val')
            tex = f"{mycpu}/{mycpukernel}"
            #metric = root.find(".//metric[@expression]").set('expression', tex)

            data['metric'].append({
                'Name': metric_name,
                'Expression': tex,
                'Type': element_type
            })

        return data
    
if __name__ == "__main__":
    p = Whatever()
    res = p.parse_xml_to_json()
    print(res)

Output:

{'metric': [{'Name': 'Ratio', 'Expression': '73002/73003', 'Type': 'pqr'}]}