added ability to parse attributes and elements containing no children or data to...
[OpenSRF.git] / src / python / osrf / utils.py
1 import xml.dom.minidom, re
2
3 def osrfXMLFileToObject(filename):
4     """Turns the contents of an XML file into a Python object"""
5     doc = xml.dom.minidom.parse(filename)
6     obj = osrfXMLNodeToObject(doc.documentElement)
7     doc.unlink()
8     return obj
9
10 def osrfXMLStringToObject(string):
11     """Turns an XML string into a Python object"""
12     doc = xml.dom.minidom.parseString(string)
13     obj = osrfXMLNodeToObject(doc.documentElement)
14     doc.unlink()
15     return obj
16
17 def osrfXMLNodeToObject(xmlNode):
18     """Turns an XML node into a Python object"""
19     obj = {}
20
21     if xmlNode.nodeType != xmlNode.ELEMENT_NODE:
22         return obj
23
24     done = False
25     nodeName = xmlNode.nodeName
26
27     for nodeChild in xmlNode.childNodes:
28         if nodeChild.nodeType == xmlNode.ELEMENT_NODE:
29             subObj = osrfXMLNodeToObject(nodeChild);
30             __appendChildNode(obj, nodeName, nodeChild.nodeName, subObj)
31             done = True
32
33     for attr in xmlNode.attributes.values():
34         __appendChildNode(obj, nodeName, attr.name, dict([(attr.name, attr.value)]))
35         
36
37     if not done and len(xmlNode.childNodes) > 0:
38         # If the node has no element children, clean up the text 
39         # content and use that as the data
40         textNode = xmlNode.childNodes[0] # extract the text node
41         data = unicode(textNode.nodeValue).replace('^\s*','')
42         data = data.replace('\s*$','')
43
44         if nodeName in obj:
45             # the current element contains attributes and text
46             obj[nodeName]['#text'] = data
47         else:
48             # the current element contains text only
49             obj[nodeName] = data
50
51     return obj
52
53
54 def __appendChildNode(obj, nodeName, childName, subObj):
55     """ If a node has element children, create a new sub-object 
56         for this node, attach an array for each type of child
57         and recursively collect the children data into the array(s) """
58
59     if not obj.has_key(nodeName):
60         obj[nodeName] = {}
61
62     if not obj[nodeName].has_key(childName):
63         # we've encountered 1 sub-node with nodeChild's name
64         if childName in subObj:
65             obj[nodeName][childName] = subObj[childName]
66         else:
67             obj[nodeName][childName] = None
68
69     else:
70         if isinstance(obj[nodeName][childName], list):
71             # we already have multiple sub-nodes with nodeChild's name
72             obj[nodeName][childName].append(subObj[childName])
73
74         else:
75             # we already have 1 sub-node with nodeChild's name, make 
76             # it a list and append the current node
77             val = obj[nodeName][childName]
78             obj[nodeName][childName] = [ val, subObj[childName] ]
79
80
81 def osrfObjectFindPath(obj, path, idx=None):
82     """Searches an object along the given path for a value to return.
83
84     Path separaters can be '/' or '.', '/' is tried first."""
85
86     parts = []
87
88     if re.search('/', path):
89         parts = path.split('/')
90     else:
91         parts = path.split('.')
92
93     for part in parts:
94         try:
95             o = obj[part]
96         except Exception:
97             return None
98         if isinstance(o,str): 
99             return o
100         if isinstance(o,list):
101             if( idx != None ):
102                 return o[idx]
103             return o
104         if isinstance(o,dict):
105             obj = o
106         else:
107             return o
108
109     return obj
110
111
112             
113