2

I need some help. I have this XML:

<?xml version="1.0"?>
<WDAT>
<EMOD>
      <MATERIAL number="1.2345">
            <Values>
                      <X>20.0</X>
                      <Y>200.0</Y>
            </Values>
            <Values>
                      <X>100.0</X>
                      <Y>190.0</Y>
            </Values>
            <Values>
                      <X>200.0</X>
                      <Y>185.0</Y>
            </Values>
            <Values>
                      <X>300.0</X>
                      <Y>180.0</Y>
            </Values>
            <Values>
                      <X>400.0</X>
                      <Y>175.0</Y>
            </Values>
    </MATERIAL>
</EMOD>
</WDAT>

I want to get an array with the X-Y values for the material number 1.2345, for example.

tree_materials = ET.parse(r"materials.xml")
root_materials = tree_materials.getroot()
materials = root_materials.findall('EMOD/MATERIAL')
material = tree_materials.findall(".//MATERIAL[@number='1.2345']")
valuess = material.findall('X')

The variable "material" has the correct memory address, but I have difficulties to get the values from X and Y.

Thanks.

Rafael

3 Answers 3

1

Underneath 'MATERIAL', you have two levels - so you need to pass both levels to findall. You could do:

material = tree_materials.find(".//MATERIAL[@number='1.2345']")
lx = material.findall('Values/X')
ly = material.findall('Values/Y')
points = zip([x.text for x in lx], [y.text for y in ly])

However, you really have to trust your XML to do that - if someone accidentally gave you a file like this:

        <Values>
                  <X>200.0</X>
                  <Y>185.0</Y>
        </Values>
        <Values>
                  <Y>180.0</Y>
        </Values>
        <Values>
                  <X>400.0</X>
                  <Y>175.0</Y>
        </Values>

then you would end up with (200.0, 185.0), (180.0, 175.0) and since zip stops at the shortest item, the 400.0 would just get silently dropped and you might not even know. Explicit is better:

values = material.findall('Values')
points = [(v.find('X').text, v.find('Y').text) for v in values]

If an X or Y node is not present, this will throw an exception, and you'll see it immediately.

You'll need to do another list comprehension to cast these to float, but you can already do that.

Sign up to request clarification or add additional context in comments.

Comments

1

In the last 2 lines you can do the following:

    material = tree_materials.find(".//MATERIAL[@number='1.2345']")
    xs = material.findall('X')
    ys = material.findall('Y')
    values = zip([x.text for x in xs], [x.text for x in xs])

this will output the list of tuples like [("20.0", "200.0")...]

3 Comments

Thanks for the quick answer. I checked the memory address of "material ", it is ok. But I get an error in the last line: KeyError: '()'
I get now "AttributeError: 'list' object has no attribute 'text'". I am near to the solution, but I dont understand why dont works. I know, it has to be easy.... i am learning. Thanks.
Sorry, that was my fault. I was to lazy to check the solution first.
0
material = tree_materials.xpath("//MATERIAL[@number='1.2345']")[0]
x_values = [x.text for x in material.xpath("//X")]
y_values = [y.text for y in material.xpath("//Y")]
xy_values = zip(x_values, y_values)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.