source: TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py @ 1904

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py@1914
Revision 1904, 9.3 KB checked in by domlowe, 13 years ago (diff)

Namespaces are now handled properly (I think!). Also added new file addnewElemTest.py to show how to add new elements to a pre-existing MOLES document

Line 
1#Dominic Lowe, BADC  18 October 2006
2#Updated Sue Latham, BADC 6 December 2006 - add dgStructuredKeyword class
3
4import cElementTree as ET
5import sys
6
7class xmlSchema(object):
8    ''' this class creates a mapping of the xml schema sequences so that it can be referred to when
9    writing out a new moles document - this enables elements to be written in the order specified by the schema
10    If a particular sequence mapping can't be found then the elements will be written in whichever order python sees fit.
11    It tries various searches of the schema to pick out the sequences (which can be nested)
12    '''
13    def __init__(self, schema):
14        self.sequences={}
15        def __addSequence(molesname, sequence):
16            '''adds a new sequences to the list, also handles xs:choice'''
17            seqlist=[]
18            for elem in sequence[:]:
19                if elem.attrib.has_key('name'):
20                    seqlist.append(elem.attrib['name'])
21                if elem.attrib.has_key('ref'):
22                    seqlist.append(elem.attrib['ref'].split(':')[1])
23                if elem.tag == '{http://www.w3.org/2001/XMLSchema}choice':
24                    for subelem in elem[:]:
25                        if subelem.attrib.has_key('name'):
26                            seqlist.append(subelem.attrib['name'])
27            self.sequences[molesname]=seqlist
28
29        for event, elem in ET.iterparse(schema):
30            '''look for sequences in complexTypes'''
31            if elem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
32                if elem.attrib.has_key('name'):
33                    molesclassname= elem.attrib['name']
34                    if elem.attrib['name'][-4:]=='Type':
35                        molesclassname=molesclassname[:-4]
36                    for subelem in elem[:]:
37                        if subelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
38                            __addSequence(molesclassname,subelem)
39            if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
40                '''Look for parents of complex types - sometimes a complexType is declared but does not have a name and
41              the name belongs to the parent 'element' element.'''
42                if elem.attrib.has_key('name'):
43                    molesclassname= elem.attrib['name']
44                    for subelem in elem[:]:
45                        if subelem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
46                            for subsubelem in subelem[:]:
47                                if subsubelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
48                                    __addSequence(molesclassname,subsubelem)
49            '''this bit handles the use of types. If some element has a name and a type attribute then if that type is complex it has
50            the same sequence as the type e.g. <xs:element name="dataModelID" type="moles:dgMetadataIDType">
51            need to run iterparse again as this needs to be done when all elements have already been parsed. '''
52            for event, elem in ET.iterparse(schema):
53                if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
54                    if elem.attrib.has_key('name'):
55                        molesclassname= elem.attrib['name']
56                        if elem.attrib.has_key('type'):
57                            typename=elem.attrib['type']
58                            if typename[-4:]=='Type':
59                                 typename=typename[:-4].split(':')[1]  #may also be prefixed by moles
60                            try:
61                                seq=self.sequences[typename]
62                                self.sequences[molesclassname]=seq
63                            except KeyError:
64                                pass  #can't find anything
65       
66           
67    def lookupOrder(self, dict,classname):
68        '''takes the attributes in a dictionary and orders them according to the schema sequence'''
69        try:
70            order=self.sequences[classname]
71        except KeyError:
72            order = []
73            for key in dict:
74                order.append(dict[key]) # if it can't be found an unordered list is returned from the original dictionary items
75        return order
76
77class molesElement(object):
78    ''' molesElement class - base class of all elements '''
79    def __init__(self, namespace=None, **kwargs):
80        if namespace !=None:
81            self.ns=namespace
82        else:
83            self.ns = '{http://ndg.nerc.ac.uk/moles}'
84        self.__dict__.update(kwargs)
85   
86    def __combineattributes(self,attname, newChild):
87        att = getattr(self,attname)
88        if isinstance(att,molesElement):
89            setattr(self,attname,[att, newChild])
90        else:
91            att.append(newChild)
92            setattr(self, attname,att)
93           
94    def _stripNS(self, tagtostrip):
95        try:
96            elemname=tagtostrip.split('}')[1]
97            ns=tagtostrip.split('}')[0]+'}'
98        except IndexError:
99            elemname=tagtostrip
100            ns='{https://ndg.nerc.ac.uk/moles}'
101        return elemname, ns
102           
103        ns=tagtostrip.split('}')[1]
104     
105   
106    def addChildElem(self, childname, childobj):
107        #sometimes you want to add a child element but don't know if there is one already. In which case you want to create a list of child objects.
108        if hasattr(self, childname):
109            currentattribute=getattr(self,childname)
110            if type(getattr(self,childname)) is list:
111                currentattribute.append(childobj)
112            else:
113                newlist=[currentattribute]
114                newlist.append(childobj)
115                setattr(self,childname, newlist)
116        else:
117            setattr(self,childname, childobj)
118   
119    def toXML(self,molesFrag, schema=None):
120        if schema != None:
121            self.schema=schema
122        else:
123            self.schema=None
124        orderedAttribs=schema.lookupOrder(self.__dict__,molesFrag.tag)
125        print self.__dict__
126        for item in orderedAttribs:
127           if hasattr(self, item):
128                if isinstance(self.__dict__[item], molesElement):
129                    frag=ET.Element(item)
130                    self.__dict__[item].toXML(frag,schema=self.schema)
131                    molesFrag.append(frag)
132                elif isinstance(self.__dict__[item], list):
133                    for it in self.__dict__[item]:
134                        if isinstance(it, molesElement):
135                            frag=ET.Element(item)
136                            it.toXML(frag, schema=self.schema)
137                            molesFrag.append(frag)
138                        else:
139                            frag=ET.Element(item)
140                            frag.text=it
141                else:
142                    frag=ET.Element(item)
143                    frag.text=self.__dict__[item]
144                    molesFrag.append(frag)
145        return molesFrag
146           
147    def fromXML(self,molesFrag):
148        children = molesFrag.getchildren()
149       
150        if children ==[]:
151            elementWithoutNS, ns=self._stripNS(molesElement.tag)
152            setattr(self,elementWithoutNS, molesElement.text)
153        if children!=[]:
154            for child in children:
155                if child.getchildren()!=[]:
156                    childWithoutNS, ns=self._stripNS(child.tag)
157                    newClass=type(childWithoutNS, (molesElement,),{})
158                    newChild=newClass(ns)
159                    newChild.fromXML(child)
160                    kw=child.tag
161                    if hasattr(self, childWithoutNS):
162                        self.__combineattributes(childWithoutNS, newChild)
163                    else:
164                        setattr(self,childWithoutNS, newChild)
165                else:
166                    childWithoutNS, ns=self._stripNS(child.tag)
167                    setattr(self,childWithoutNS, child.text)
168       
169                   
170class dgMetadata(molesElement):   
171    def __init__(self, **kwargs):
172        molesElement.__init__(self, **kwargs)
173       
174    def toXML(self):
175        self.schema = xmlSchema('ndgmetadata1.3.xsd')
176        molesFrag=ET.Element('dgMetadata')
177        molesFrag.attrib['xmlns']='http://ndg.nerc.ac.uk/moles'
178        molesElement.toXML(self,molesFrag,schema=self.schema)
179        return molesFrag
180
181
182class MolesDoc(object):
183    def __init__(self):
184        self._createClasses()
185   
186    def _create_a_class(self,name, base_class):
187        aNewClass=type(name, (base_class,),{})
188        return aNewClass
189
190    def _createClasses(self):
191        #if you want more classes just add their names to this list
192        #could probably examine the schema here....
193        classList= \
194        ['dataModelID', \
195        'dgDataGranule', \
196        'dgDataEntity', \
197        'dgMetadataRecord', \
198        'dgMetadataID',
199        'dgCoverage', \
200        'dgSpatioTemporalCoverage', \
201        'dgSpatialCoverage', \
202        'dgTemporalCoverage', \
203        'dgBoundingBox', \
204        'DateRange', \
205        'dgDataSummary',\
206        'dgParameterSummary',\
207        'dgRangeDataParameter',\
208        'dgStdParameterMeasured',\
209        'dgStandardUnit',\
210        'dgValidTermID',\
211        'metadataDescriptionID',\
212        'dgMetadataDescription', \
213        'dgStructuredKeyword', \
214        ]
215
216
217        for className in classList:
218            newClass=self._create_a_class(className, molesElement)
219            setattr(self,className,newClass)
Note: See TracBrowser for help on using the repository browser.