Posts: 212
Threads: 94
Joined: Aug 2018
Hello,
I'm using xmltodict to work with XML files. It turns data into nested dictionaries.
In nested dictionaries that don't contain the same keys, I can't find an easy way to simply check if any given key exists somewhere.
The following functions don't work as expected:
def f(d, keys):
if not keys:
return True
return keys[0] in d and f(d[keys[0]], keys[1:])
def keys_exists(element, *keys):
'''
Check if *keys (nested) exists in `element` (dict).
'''
if not isinstance(element, dict):
raise AttributeError('keys_exists() expects dict as first argument.')
if len(keys) == 0:
raise AttributeError('keys_exists() expects at least two arguments, one given.')
_element = element
for key in keys:
try:
_element = _element[key]
except KeyError:
return False
return True Is there a way besides looping?
Thank you.
Posts: 4,787
Threads: 76
Joined: Jan 2018
Sep-17-2020, 02:36 PM
(This post was last modified: Sep-17-2020, 02:36 PM by Gribouillis.)
You can use recursion perhaps
def get_deep(element, *path):
if not path:
return element
if not isinstance(element, dict):
raise KeyError(path)
return get_deep(element[path[0]], *path[1:])
def main():
D = {
'foo': 1,
'bar': {
'baz': 'ham',
'qux': {1: 2}},
'spam': 'eggs'}
print(get_deep(D, 'bar', 'qux', 1)) # prints 2
if __name__ == '__main__':
main()
Posts: 212
Threads: 94
Joined: Aug 2018
Sep-17-2020, 02:41 PM
(This post was last modified: Sep-17-2020, 02:41 PM by Winfried.)
Thanks, but I had in mind something like
if key_exists(myarray,"mykey"):
print("Found key")
else:
print("Key not in array") ie. just giving the array and the key, regardless of where it's located.
Impossible?
Posts: 1,838
Threads: 2
Joined: Apr 2017
It's still a good fit for recursion though, due to the nesting.
Posts: 212
Threads: 94
Joined: Aug 2018
Yes, I'll use it if nothing else comes up. Thank you.
Posts: 4,787
Threads: 76
Joined: Jan 2018
Sep-17-2020, 03:03 PM
(This post was last modified: Sep-17-2020, 03:03 PM by Gribouillis.)
Here is a recursive single key search
def key_exists(element, key):
if isinstance(element, dict):
if key in element:
return True
else:
return any(key_exists(e, key) for e in element.values())
else:
return False
Posts: 212
Threads: 94
Joined: Aug 2018
For some reason, it finds 'Placemark', but not 'LineString', altough both are nested dictionaries:
print(key_exists2(doc, 'Placemark'))
#True
print(key_exists2(doc, 'LineString'))
#False input.kml:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Document.kml</name>
<open>1</open>
<Style id="exampleStyleDocument">
<LabelStyle>
<color>ff0000cc</color>
</LabelStyle>
</Style>
<Placemark>
<name>Document Feature 1</name>
<styleUrl>#exampleStyleDocument</styleUrl>
<Point>
<coordinates>-122.371,37.816,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Document Feature 2</name>
<styleUrl>#exampleStyleDocument</styleUrl>
<Point>
<coordinates>-122.370,37.817,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>My track</name>
<LineString>
<coordinates>-0.376291,43.296237,199.75
-0.377381,43.29405</coordinates>
</LineString>
</Placemark>
</Document>
</kml>
Posts: 1,583
Threads: 3
Joined: Mar 2020
But not everything is a dictionary. Because there are multiple Placemark sections, when you request that key, you get back a list of all sections. Then the code checks, finds it's not a dictionary, and stops. It would need to also iterate through lists to find elements inside sections with repeated names.
>>> type(x['kml']['Document']['Placemark'])
<class 'list'>
>>> print(*x['kml']['Document']['Placemark'], sep='\n')
OrderedDict([('name', 'Document Feature 1'), ('styleUrl', '#exampleStyleDocument'), ('Point', OrderedDict([('coordinates', '-122.371,37.816,0')]))])
OrderedDict([('name', 'Document Feature 2'), ('styleUrl', '#exampleStyleDocument'), ('Point', OrderedDict([('coordinates', '-122.370,37.817,0')]))])
OrderedDict([('name', 'My track'), ('LineString', OrderedDict([('coordinates', '-0.376291,43.296237,199.75\n -0.377381,43.29405')]))])
Posts: 212
Threads: 94
Joined: Aug 2018
Sep-17-2020, 04:06 PM
(This post was last modified: Sep-17-2020, 04:06 PM by Winfried.)
Thanks for the infos.
As a work-around, I'll just search in the source file before it's parsed:
with open(item) as f: xml = f.read()
if 'LineString' in xml:
print("Found LS")
else:
print("No LS")
Posts: 6,790
Threads: 20
Joined: Feb 2020
Maybe your approach is wrong. Instead of having the code look for keys I would write the code so the keys dictate what the code does.
|