<p>
If Untangle is too simple for your XML parsing needs, check out xmltodict. Like untangle, xmltodict is simpler than the usual suspects (lxml, beautiful soup), but it's got some advanced features as well.
</p>
<p>If you're reading this article, I assume you've read at least the introduction to my article about <a href="https://hackerpublicradio.org/eps.php?id=2012">Untangle</a>, and you should probably also read, at some point, my article on using <a href="https://hackerpublicradio.org/eps.php?id=2010">JSON</a> just so you know your options.</p>
<p>Quick re-cap about XML:</p>
<p>XML is a way of storing data in a hierarchical arrangement so that the data can be parsed later. It's explicit and strictly structured, so one of its benefits is that it paints a fairly verbose definition of data. Here's an example of some simple XML:</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;book&gt;
   &lt;chapter id=&quot;prologue&quot;&gt;
      &lt;title&gt;
     The Beginning
  &lt;/title&gt;
      &lt;para&gt;
     This is the first paragraph.
      &lt;/para&gt;
    &lt;/chapter&gt;

    &lt;chapter id=&quot;end&quot;&gt;
      &lt;title&gt;
     The Ending
  &lt;/title&gt;
      &lt;para&gt;
     Last para of last chapter.
      &lt;/para&gt;
    &lt;/chapter&gt;
&lt;/book&gt;</code></pre>
<p>And here's some info about the <code>xmltodict</code> library that makes parsing that a lot easier than the built-in Python tools:</p>
<h2 id="install">Install</h2>
<p>Install <code>xmltodict</code> manually, or from your repository, or using <code>pip</code>:</p>
<pre><code>$ pip install xmltodict</code></pre>
<p>or if you need to install it locally:</p>
<pre><code>$ pip install --user xmltodict</code></pre>
<h2 id="xmltodict">Xmltodict</h2>
<p>With <code>xmltodict</code>, each element in an XML document gets converted into a dictionary (specifically an <code>OrderedDictionary</code>), which you then treat basically the same as you would JSON (or any Python OrderedDict).</p>
<p>First, ingest the XML document. Assuming it's called <code>sample.xml</code> and is located in the current directory:</p>
<pre><code>&gt;&gt;&gt; import xmltodict
&gt;&gt;&gt; with open(&#39;sample.xml&#39;) as f:
...     data = xmltodict.parse(f.read())</code></pre>
<p>If you're a visual thinker, you might want or need to see the data. You can look at it just by dumping data:</p>
<pre><code>&gt;&gt;&gt; data
OrderedDict([(&#39;book&#39;, OrderedDict([(&#39;chapter&#39;,
[OrderedDict([(&#39;@id&#39;, &#39;prologue&#39;),
(&#39;title&#39;, &#39;The Beginning&#39;),
...and so on...</code></pre>
<p>Not terribly pretty to look at. Slightly less ugly is your data set piped through <code>json.dumps</code>:</p>
<pre><code>&gt;&gt;&gt; import json
&gt;&gt;&gt; json.dumps(data)
&#39;{&quot;book&quot;: {&quot;chapter&quot;: [{&quot;@id&quot;: &quot;prologue&quot;,
&quot;title&quot;: &quot;The Beginning&quot;, &quot;para&quot;: &quot;This is the first paragraph.&quot;},
{&quot;@id&quot;: &quot;end&quot;, &quot;title&quot;: &quot;The Ending&quot;,
&quot;para&quot;: &quot;This is the last paragraph of the last chapter.&quot;}]
}}&#39;</code></pre>
<p>You can try other feats of pretty printing, if they help:</p>
<pre><code>&gt;&gt;&gt; pp = pprint.PrettyPrinter(indent=4)
&gt;&gt;&gt; pp.pprint(data)
{ &#39;book&#39;: { &#39;chapter&#39;: [{&#39;@id&#39;: &#39;prologue&#39;,
                         &#39;title&#39;: &#39;The Beginning&#39;,
             &#39;para&#39;: &#39;This is the ...
                         ...and so on...                 </code></pre>
<p>More often than not, though, you're going to be &quot;walking&quot; the XML tree, looking for specific points of interest. This is fairly easy to do, as long as you remember that syntactically you're dealing with a Python dict, while structurally, inheritance matters.</p>
<h3 id="elements-tags">Elements (Tags)</h3>
<p>Exploring the data element-by-element is very easy. Calling your data set by its root element (in our current example, that would be <code>data['book']</code>) would return the entire data set under the <code>book</code> tag. We'll skip that and drill down to the <code>chapter</code> level:</p>
<pre><code>&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;]
[OrderedDict([(&#39;@id&#39;, &#39;prologue&#39;), (&#39;title&#39;, &#39;The Beginning&#39;),
(&#39;para&#39;, &#39;This is the first paragraph.&#39;)]),
OrderedDict([(&#39;@id&#39;, &#39;end&#39;), (&#39;title&#39;, &#39;The Ending&#39;),
(&#39;para&#39;, &#39;Last paragraph of last chapter.&#39;)])]</code></pre>
<p>Admittedly, it's still a lot of data to look at, but you can see the structure.</p>
<p>Since we have two chapters, we can enumerate which chapter to select, if we want. To see the zeroeth chapter:</p>
<pre><code>&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;][0]
OrderedDict([(&#39;@id&#39;, &#39;prologue&#39;),
(&#39;title&#39;, &#39;The Beginning&#39;),
(&#39;para&#39;, &#39;This is the first paragraph.&#39;)])</code></pre>
<p>Or the first chapter:</p>
<pre><code>&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;][1]
OrderedDict([(&#39;@id&#39;, &#39;end&#39;), (&#39;title&#39;, &#39;The Ending&#39;),
(&#39;para&#39;, &#39;Last paragraph of last chapter.&#39;)])</code></pre>
<p>And of course, you can continue narrowing your focus:</p>
<pre><code>&gt;&gt;&gt; data[&quot;book&quot;][&quot;chapter&quot;][0][&#39;para&#39;]
&#39;This is the first paragraph.&#39;</code></pre>
<p>It's sort of like Xpath for toddlers. Having had to work with Xpath, I'm happy to have this option.</p>
<h3 id="attributes">Attributes</h3>
<p>You may have already noticed that in the dict containing our data, there is some special notation happening. For instance, there is no <code>@id</code> element in our XML, and yet that appears in the dict.</p>
<p><code>Xmltodict</code> uses the <code>@</code> symbol to signify an attribute of an element. So to look at the attribute of an element:</p>
<pre><code>&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;][0][&#39;@id&#39;]
&#39;prologue&#39;</code></pre>
<p>If you need to see each attribute of each chapter tag, just iterate over the dict. A simple example:</p>
<pre><code>&gt;&gt;&gt; for c in range(0,2):
...     data[&#39;book&#39;][&#39;chapter&#39;][c][&#39;@id&#39;]
...
&#39;prologue&#39;
&#39;end&#39;</code></pre>
<h3 id="contents">Contents</h3>
<p>In addition to special notation for attributes, <code>xmltodict</code> uses the <code>#</code> prefix to denote contents of complex elements. To show this example, I'll make a minor modification to <code>sample.xml</code>:</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;book&gt;
   &lt;chapter id=&quot;prologue&quot;&gt;
      &lt;title&gt;
     The Beginning
  &lt;/title&gt;
      &lt;para class=&quot;linux&quot;&gt;
     This is the first paragraph.
      &lt;/para&gt;
    &lt;/chapter&gt;

    &lt;chapter id=&quot;end&quot;&gt;
      &lt;title&gt;
     The Ending
  &lt;/title&gt;
      &lt;para class=&quot;linux&quot;&gt;
     Last para of last chapter.
      &lt;/para&gt;
    &lt;/chapter&gt;
&lt;/book&gt;</code></pre>
<p>Notice that the <code>&lt;para&gt;</code> elements now have a <code>linux</code> attribute, and also contain text content (unlike <code>&lt;chapter&gt;</code> elements, which have attributes but only contain other elements).</p>
<p>Look at this data structure:</p>
<pre><code>&gt;&gt;&gt; import xmltodict
&gt;&gt;&gt; with open(&#39;sample.xml&#39;) as g:
...     data = xmltodict.parse(g.read())
&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;][0]
OrderedDict([(&#39;@id&#39;, &#39;prologue&#39;),
(&#39;title&#39;, &#39;The Beginning&#39;),
(&#39;para&#39;, OrderedDict([(&#39;@class&#39;, &#39;linux&#39;),
(&#39;#text&#39;, &#39;This is the first paragraph.&#39;)]))])</code></pre>
<p>There is a new entry in the dictionary: <code>#text</code>. It contains the text content of the <code>&lt;para&gt;</code> tag and is accessible in the same way that an attribute is:</p>
<pre><code>&gt;&gt;&gt; data[&#39;book&#39;][&#39;chapter&#39;][0][&#39;para&#39;][&#39;#text&#39;]
&#39;This is the first paragraph.&#39;</code></pre>
<h3 id="advanced">Advanced</h3>
<p>The <code>xmltodict</code> module supports XML namespaces and can also dump your data back into XML. For more documentation on this, have a look at the module on <a href="https://github.com/martinblech/xmltodict">github.com/martinblech/xmltodict</a>.</p>
<h2 id="what-to-use">What to Use?</h2>
<p>Between <code>untangle</code>, <code>xmltodict</code>, and JSON, you have pretty good set of options for data parsing. There really are diferent uses for each one, so there's not necessarily a &quot;right&quot; or &quot;wrong&quot; answer. Try them out, see what you prefer, and use what is best. If you don't know what's best, use what you're most comfortable with; you can always improve it later.</p>
<p>[EOF]</p>
<p>Made on Free Software.</p>
