[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 166
  • Last Modified:

ElementTree sub elements

Learning python while experimenting with ElementTree... That aside, reference attached.   Code currently prints None for elements
'poi.upperLeftLat'
'poi.upperLeftLon'
'poi.bottomRightLat'
'poi.bottomRightLon'

Need modification to source to display values.   Thanks
views.py
0
forums_mp
Asked:
forums_mp
  • 3
  • 2
1 Solution
 
peprCommented:
The XPath expresion must be written differently. The 'poi.bottomRightLat' is not correct. I also suggest to use the namespaces argument of the element.find() and to replace fiddling with qualified names by explicit usage of the namespace prefix (here sr: was used as in the source of the example, but it can be named differently). See the simplified code below. Notice the self.tag_text() of the Source class (you may want to add some error checking inside) and how it is used in the __init__():
from __future__ import print_function
import xml.etree.ElementTree as ET
import os

class InvalidTagError(Exception):
    def __init__(self, element, expected):
        self.element = element
        self.expected = expected
    def __str__(self):
        return "'{0}' is not an instance of '{1}'".format(self.element, self.expected)


class Source(object):
    def __init__(self, el, ns=None):
        self.el = el
        self.namespaces = { 'sr': ns }
        ##print(el.tag)
        if not self.el.tag.endswith('}streamInfo'):  # dirty hack
            raise InvalidTagError(self.el, 'streamInfo')

        self.keyword = self.el.get('keyword', None)
        self.sid = self.el.get('sid', None)
        self.mediaID = self.tag_text('sr:mediaID')
        self.timeStamp = self.tag_text('sr:timeStamp')
        self.sourceInterface = self.tag_text('sr:sourceInterface')
        self.destInterface = self.tag_text('sr:destInterface')
        self.streamDistribution = self.tag_text('sr:streamDistribution')
        self.sourceStatus = self.tag_text('sr:sourceStatus')
        self.upperLeftLat = self.tag_text('./sr:poi/sr:upperLeftLat')
        self.upperLeftLon = self.tag_text('./sr:poi/sr:upperLeftLon')
        self.bottomRightLat = self.tag_text('./sr:poi/sr:bottomRightLat')
        self.bottomRightLon = self.tag_text('./sr:poi/sr:bottomRightLon')

    def tag_text(self, tag):
        return self.el.find(tag, self.namespaces).text
        

def main():
    xml = '''<?xml version="1.0"?>
    <sr:Media xmlns:sr="http://this/prefix/need/to/be/defined">
      <sr:streamInfo keyword="Test" sid="6ab77fb0-ea08-4843-8505-3b87e95ac39f">
        <sr:mediaID>6ab77fb0-ea08-4843-8505-3b87e95ac39f</sr:mediaID>
        <sr:timeStamp>13084444444</sr:timeStamp>
        <sr:sourceInterface>udp://@232.1.1.2:1834</sr:sourceInterface>
        <sr:destInterface>udp://@232.1.1.2:1834</sr:destInterface>
        <sr:streamDistribution>live</sr:streamDistribution>
        <sr:sourceStatus>Active</sr:sourceStatus>
        <sr:poi>
          <sr:upperLeftLat>1920</sr:upperLeftLat>
          <sr:upperLeftLon>1920</sr:upperLeftLon>
          <sr:bottomRightLat>1920</sr:bottomRightLat>
          <sr:bottomRightLon>1920</sr:bottomRightLon>
        </sr:poi>
      </sr:streamInfo>
      <sr:streamInfo keyword="Other test" sid="2df33ac1-ea08-4843-2525-3b87e95ac39f">
        <sr:mediaID>2df33ac1-ea08-4843-6321-3b87e95ac39f</sr:mediaID>
        <sr:timeStamp>13086444578</sr:timeStamp>
        <sr:sourceInterface>tcp://@232.1.1.2:1834</sr:sourceInterface>
        <sr:destInterface>udp://@232.1.1.2:1834</sr:destInterface>
        <sr:streamDistribution>live</sr:streamDistribution>
        <sr:sourceStatus>Active</sr:sourceStatus>
        <sr:poi>
          <sr:upperLeftLat>1920</sr:upperLeftLat>
          <sr:upperLeftLon>1080</sr:upperLeftLon>
          <sr:bottomRightLat>480</sr:bottomRightLat>
          <sr:bottomRightLon>640</sr:bottomRightLon>
        </sr:poi>
      </sr:streamInfo>
    </sr:Media>'''
    root = ET.fromstring(xml)
    ns = 'http://this/prefix/need/to/be/defined'
    sources = []
    for element in root.findall('sr:streamInfo', {'sr': ns }):
        ##ET.dump(element)
        sources.append(Source(element, ns))

    for source in sources:
        print()
        ##ET.dump(source.el)
        print(source.el)
        print("mediaID =", source.mediaID)
        print("keyword =", source.keyword )
        print("sid =", source.sid )
        print("upperLeftLat =", source.upperLeftLat )
        print("upperLeftLon =", source.upperLeftLon )
        print("bottomRightLat =", source.bottomRightLat )
        print("bottomRightLon =", source.bottomRightLon )

if __name__ == '__main__':
    main()

Open in new window

The script prints
c:\_Python\forums_mp\Q_28479377>py views2.py

<Element '{http://this/prefix/need/to/be/defined}streamInfo' at 0x23440f0>
mediaID = 6ab77fb0-ea08-4843-8505-3b87e95ac39f
keyword = Test
sid = 6ab77fb0-ea08-4843-8505-3b87e95ac39f
upperLeftLat = 1920
upperLeftLon = 1920
bottomRightLat = 1920
bottomRightLon = 1920

<Element '{http://this/prefix/need/to/be/defined}streamInfo' at 0x23446a0>
mediaID = 2df33ac1-ea08-4843-6321-3b87e95ac39f
keyword = Other test
sid = 2df33ac1-ea08-4843-2525-3b87e95ac39f
upperLeftLat = 1920
upperLeftLon = 1080
bottomRightLat = 480
bottomRightLon = 640

Open in new window

0
 
forums_mpAuthor Commented:
I've increased the points given I have two follow on questions - one of which requires python code for you
a) When you say: 'The 'poi.bottomRightLat' is not correct.'.    bottomRightLat only or all (upperLeftLat, etc) poi elements?
b) Consider.

    xml = '''<?xml version="1.0" encoding="UTF-8" ?>
      <br:Terminal xmlns:br="http://this/prefix/need/to/be/defined"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <br:MediaSource>
          <br:MediaSourceType>
            <br:IPSource xsi:nil="true">
              <br:Interface>225.10.10.1</br:Interface>
              <br:Port>1234</br:Port>
              <br:TTL xsi:nil="true"/>
            </br:IPSource>
            <br:FileSource xsi:nil ="true">
            </br:FileSource>
          </br:MediaSourceType>
          <br:EngineName xsi:nil="true"/>
          <br:Name xsi:nil="true"/>
          <br:Description xsi:nil="true"/>
          <br:Program xsi:nil="true"/>
          <br:MediaFormatType xsi:nil="true"/>
        </br:MediaSource>
        <br:Archive xsi:nil ="true">
          <br:Enabled>true</br:Enabled>
          <br:MediaID xsi:nil ="true"/>
        </br:Archive>
      </br:Terminal>'''

Open in new window


There's optional (xsi:nil='true") elements reflected in XML above.    I'm using a library called POCO in C++ and with POCO modify optional elements becomes easy

void Test
  ( Poco::AutoPtr < Poco::Util::XMLConfiguration > ptrCfg )
{
  std::string const optionalStringIPSource
    ( "br:MediaSource.br:MediaSourceType.br:IPSource[@xsi:nil]" );
  if ( ptrCfg->has ( optionalStringIPSource ) ) {
    ptrCfg->remove ( optionalStringIPSource );
    std::string const optionalStringTTL
      ( "br:MediaSource.br:MediaSourceType.br:IPSource.br:TTL[@xsi:nil]" );
    if ( ptrCfg->has ( optionalStringTTL ) ) {
    	std::cout << " OK" << std::endl;
      ptrCfg->remove ( optionalStringTTL );
      ptrCfg->setInt ( "br:MediaSource.br:MediaSourceType.br:IPSource.br:TTL" ,
    		            5  );
    }
  }

}

Open in new window


In doing so IPSource now becomes

            <br:IPSource>
              <br:Interface>225.10.10.1</br:Interface>
              <br:Port>1234</br:Port>
              <br:TTL>5</br:TTL>
            </br:IPSource>

Open in new window


What's the python/ElementTree equivalent?
0
 
peprCommented:
Ad a) I mean the syntax of the XPath expression in 'poi.bottomRightLat' to refer that element is not correct (the dot). It holds for the other elements as well, of course.

Ad b) Try the following code:
from __future__ import print_function
import xml.etree.ElementTree as ET

xml = '''<?xml version="1.0" encoding="UTF-8" ?>
  <br:Terminal xmlns:br="http://this/prefix/need/to/be/defined"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <br:MediaSource>
      <br:MediaSourceType>
        <br:IPSource xsi:nil="true">
          <br:Interface>225.10.10.1</br:Interface>
          <br:Port>1234</br:Port>
          <br:TTL xsi:nil="true"/>
        </br:IPSource>
        <br:FileSource xsi:nil ="true">
        </br:FileSource>
      </br:MediaSourceType>
      <br:EngineName xsi:nil="true"/>
      <br:Name xsi:nil="true"/>
      <br:Description xsi:nil="true"/>
      <br:Program xsi:nil="true"/>
      <br:MediaFormatType xsi:nil="true"/>
    </br:MediaSource>
    <br:Archive xsi:nil ="true">
      <br:Enabled>true</br:Enabled>
      <br:MediaID xsi:nil ="true"/>
    </br:Archive>
  </br:Terminal>'''
namespaces = {'br': 'http://this/prefix/need/to/be/defined',
              'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
root = ET.fromstring(xml)

# Find the IPSource element with the 
IPSource = root.find('.//br:MediaSource/br:MediaSourceType/br:IPSource[@xsi:nil]', namespaces)
ET.dump(IPSource)
print('--------------')

# Delete the nil attribute. I am not aware of a function for deleting the attribute of the 
# element with the namespaces given as a separate argument. Here I a constructing the 
# attribute full name.
xsi_nil = '{{{}}}nil'.format(namespaces['xsi'])
del IPSource.attrib[xsi_nil]
ET.dump(IPSource)
print('--------------')
                                          
# Find the TTL inside.
TTL = IPSource.find('./br:TTL', namespaces)
ET.dump(TTL)
print('--------------')

# Remove the xsi:nil.
del TTL.attrib[xsi_nil]
ET.dump(TTL)
print('--------------')

# Set the text value.
TTL.text = '5'
ET.dump(TTL)
print('--------------')

# Dump it from the root.
ET.dump(root)

Open in new window

It prints
<ns0:IPSource xmlns:ns0="http://this/prefix/need/to/be/defined" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true">
          <ns0:Interface>225.10.10.1</ns0:Interface>
          <ns0:Port>1234</ns0:Port>
          <ns0:TTL xsi:nil="true" />
        </ns0:IPSource>

--------------
<ns0:IPSource xmlns:ns0="http://this/prefix/need/to/be/defined" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <ns0:Interface>225.10.10.1</ns0:Interface>
          <ns0:Port>1234</ns0:Port>
          <ns0:TTL xsi:nil="true" />
        </ns0:IPSource>

--------------
<ns0:TTL xmlns:ns0="http://this/prefix/need/to/be/defined" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />

--------------
<ns0:TTL xmlns:ns0="http://this/prefix/need/to/be/defined" />

--------------
<ns0:TTL xmlns:ns0="http://this/prefix/need/to/be/defined">5</ns0:TTL>

--------------
<ns0:Terminal xmlns:ns0="http://this/prefix/need/to/be/defined" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ns0:MediaSource>
      <ns0:MediaSourceType>
        <ns0:IPSource>
          <ns0:Interface>225.10.10.1</ns0:Interface>
          <ns0:Port>1234</ns0:Port>
          <ns0:TTL>5</ns0:TTL>
        </ns0:IPSource>
        <ns0:FileSource xsi:nil="true">
        </ns0:FileSource>
      </ns0:MediaSourceType>
      <ns0:EngineName xsi:nil="true" />
      <ns0:Name xsi:nil="true" />
      <ns0:Description xsi:nil="true" />
      <ns0:Program xsi:nil="true" />
      <ns0:MediaFormatType xsi:nil="true" />
    </ns0:MediaSource>
    <ns0:Archive xsi:nil="true">
      <ns0:Enabled>true</ns0:Enabled>
      <ns0:MediaID xsi:nil="true" />
    </ns0:Archive>
  </ns0:Terminal>

Open in new window

Warning: the code does not contain testing and nesting the if's.
0
 
forums_mpAuthor Commented:
Last question on this.  I've perused the link here (https://docs.python.org/2/library/string.html) on positional arguments but still struggling with your use of {{{}}} specifically:

'{{{}}}nil'.format(namespaces['xsi'])

Open in new window


Explain ''{{{}}}nil' to me.  Trying to wrap my head around the three curly braces you supplied..Thanks
0
 
peprCommented:
The {} is the placeholder for the .format method. The {{ will produkce a single {, and the }} a single } -- they had to be doubled for not to be interpreted as parts of the placeholder syntax. The resulting string is the namespace identifier wrapped in curly braces.
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now