parsing xml find last element with matching attributes

时间:2016-02-12 20:25:50

标签: python xml parsing xpath python-3.4

I have some poorly written xml that I'm trying to loop through and extract the test name, start, and end time of each test. I use that data to append a column to a csv. My current implementation checks every element in the xml and seems to be slow.

Here is a an xml example

/*virtual*/ uint32 HyOpenGL::AddTextureArray(uint32 uiNumColorChannels, uint32 uiWidth, uint32 uiHeight, vector<unsigned char *> &vPixelData)
{
    GLenum eInternalFormat = uiNumColorChannels == 4 ? GL_RGBA8 : (uiNumColorChannels == 3 ? GL_RGB8 : GL_R8);
    GLenum eFormat = uiNumColorChannels == 4 ? GL_RGBA : (uiNumColorChannels == 3 ? GL_RGB : GL_RED);

    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, eInternalFormat, uiWidth, uiHeight, static_cast<uint32>(vPixelData.size()), 0, eFormat, GL_UNSIGNED_BYTE, NULL);

    GLuint hGLTextureArray;
    glGenTextures(1, &hGLTextureArray);
    //glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D_ARRAY, hGLTextureArray);

    // Create storage for the texture
    glTexStorage3D(GL_TEXTURE_2D_ARRAY,
                    1,                      // Number of mipmaps
                    eInternalFormat,        // Internal format
                    uiWidth, uiHeight,      // width, height
                    static_cast<uint32>(vPixelData.size()));

    for(unsigned int i = 0; i != vPixelData.size(); ++i)
    {
        // Write each texture into storage
        glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
                        0,                                      // Mipmap number
                        0, 0, i,                                // xoffset, yoffset, zoffset
                        uiWidth, uiHeight, 1,                   // width, height, depth (of texture you're copying in)
                        eFormat,                                // format
                        GL_UNSIGNED_BYTE,                       // type
                        vPixelData[i]);                         // pointer to pixel data

        GLenum eError = glGetError(); // Getting 'GL_INVALID_OPERATION' when > 1 texture depth. It's 'GL_NO_ERROR' otherwise
    }

    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return hGLTextureArray;
}

Trying to figure out how to get the Id value for the processStart that matches the name and options. So far I have:

<ProcessStart Id="1" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
<ProcessStart Id="2" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
<ProcessStart Id="3" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
<ProcessEnd Id="1" Tick="7553"/>
<ProcessEnd Id="2" Tick="7553"/>
<ProcessEnd Id="3" Tick="7553"/>
<ProcessStart Id="17" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8126" />
<ProcessStart Id="18" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8127" />
<ProcessStart Id="19" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8127" />
<ProcessEnd Id="17" Tick="12873"/>
<ProcessEnd Id="18" Tick="12873"/>
<ProcessEnd Id="19" Tick="12873"/>

but rather than checking the last element that matches the test name it check the last element of the processStart. How do I address this? Or would the fastest way to extract this information be reading the input file line by line?

New Information I want to return the Id value where Id=3. This is the last value where all ProcessStarts with matching Name and Options exist. The current count shown immediately references the last instances of ProcessStart then checks the name and options conditions. I am looking for a way to find the last ProcessStart with matching conditions.

Perhaps a better example would be to match by options as name is the same for this instance so:

root.find('ProcessStart[@Name="%s"][last()]' % test_name).get('Id')

Using this example and this data set it will return an error 'NoneType' object has no attribute 'get' I believe this is because the last element doesnt match the options. However I'm trying to target the last ProcessStart with matching options.

COMPLETE CODE:

options=" /pi=5 /m=AA"
test_id=root.find('ProcessStart[@Options="%s"][last()]' % options).get('Id')

COMPLETE XML FILE:

import xml.etree.ElementTree as ET

#Read the xml file
tree = ET.parse('C:/Users/mkaminski/Desktop/sample.xml')
root = tree.getroot()

#get the first option
test_option=root.find('ProcessStart').get('Options')
test_id=root.find('ProcessStart[@Options="%s"][last()]' % test_option).get('Id')

ERROR:

<AppLog App="RPx" Version="0.6.1" BaseVer="0.0.1" Time="20160208153547" Tick="0">
  <RPxTest TestName="Tests/WriteRead" LongName="WriteRead_b=0_pi=5_m=AA_i=0" Instances="16" Memory="49534849024" Options=" /pi=5 /m=AA" IdRange="1-17" Tick="1233" />
  <ProcessStart Id="1" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
  <ProcessStart Id="2" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
  <ProcessStart Id="3" Type="MemTest" Name="WriteRead" Options=" /pi=5 /m=AA" Tick="1233" />
  <ProcessEnd Id="1" Tick="7553"/>
  <ProcessEnd Id="2" Tick="7553"/>
  <ProcessEnd Id="3" Tick="7553"/>
  <RPxTest TestName="Tests/WriteRead" LongName="WriteRead_b=0_pi=25_m=AA_i=0" Instances="16" Memory="49534849024" Options=" /pi=25 /m=AA" IdRange="17-33" Tick="8126" />
  <ProcessStart Id="17" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8126" />
  <ProcessStart Id="18" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8127" />
  <ProcessStart Id="19" Type="MemTest" Name="WriteRead" Options=" /pi=25 /m=AA" Tick="8127" />
  <ProcessEnd Id="17" Tick="12873"/>
  <ProcessEnd Id="18" Tick="12873"/>
  <ProcessEnd Id="19" Tick="12873"/>
</AppLog>

1 个答案:

答案 0 :(得分:3)

I think that you are running into a limitation in ElementTree's xpath support (which is not complete). Using lxml your command works perfectly.

To do this with ElementTree, retrieve all of the matching elements and let Python select the last one.

Change

.bss 
     color:     resb     8

To

options=" /pi=5 /m=AA"
test_id=root.find('ProcessStart[@Options="%s"][last()]' % options).get('Id')

In this command, we get all matching ProcessStart elements (in order) as a list, and grab just the last one. Then we can grab the id.

相关问题