2015-06-12 4 views
0

Итак, я извлекаю данные из одного vShield Manager через API и создаю базу данных для синхронизации с другим ... Я успешно возвращаю XML из API (yay), и я могу разобрать его просто отлично, но это немного утомительно. Я пытаюсь создать пользовательские классы для различных объектов в vShield (виртуальные брандмауэры, глобальные объекты адресов, определения сервисов и т. Д.), Поэтому я могу правильно ссылаться на данные в моем сценарии, и именно здесь я нарваться проблемы (вроде) ...XML для пользовательского класса

Я получаю XML обратно который выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?> 
<scopingObjects> 
    <object> 
    <objectId>datacenter-2</objectId> 
    <type> 
     <typeName>Datacenter</typeName> 
    </type> 
    <name>virtualDataCenter-01</name> 
    <revision>5</revision> 
    <objectTypeName>Datacenter</objectTypeName> 
    <scope> 
     <id>group-d1</id> 
     <objectTypeName>Folder</objectTypeName> 
     <name>Datacenters</name> 
    </scope> 
    <extendedAttributes/> 
    </object> 
    <object> 
    <objectId>globalroot-0</objectId> 
    <type> 
     <typeName>GlobalRoot</typeName> 
    </type> 
    <name>Global</name> 
    <revision>460</revision> 
    <objectTypeName>GlobalRoot</objectTypeName> 
    <extendedAttributes/> 
    </object> 
</scopingObjects> 

И я itterate через это с помощью ElementTree:

# Get the list of scoping objects 
scopeObjects = [] # Declare an empty list 
r = sendRest("getScopes") 
xml = et.fromstring(r.text) 
for object in xml.iter("object"): 
    try: 
     o = scope() 
     o.create(et.tostring(object)) 
     scopeObjects.append(o) 
    except Exception, e: 
     print "Exception: " + repr(e) 

Где я использую определение следующего класса:

class scope: 
    def __init__(self): 
     self.id = "" 
     self.name = "" 
     self.type = "" 
    def create(self, xmlString): 
     try: 
      xml = et.fromstring(xmlString) 
      if xml.find("objectId") is not None: self.id = xml.find("objectId").text 
      if xml.find("name") is not None: self.name = xml.find("name").text 
      if xml.find("type/typeName") is not None: self.type = xml.find("type/typeName").text 
     except: 
      print "Error constructing scope object from:\n%s" % xmlString 
      return False 

Что все работает, до тех пор, пока я не сталкиваюсь с более сложными структурами XML ... тогда это оргия «если xml.find (« blah/blah/blah ») не является None: variable . = xml.find («бла/бла/бла») текст»снова и снова ...

Я хотел бы сделать что-то вроде:

class scope: 
    def __init__(self): 
    self.id = "" 
    self.name = "" 
    self.type = "" 
    def create(self, xmlString): 
    xmlPaths = { "id" : "objectId", "name" : "name", "type" : "type/typeName" } 
    for key in xmlPaths.keys(): 
     element = xml.find(xmlPaths[key]) 
     if element is not None: 
     # Set the class property named by "key" to the value of element.text 
     locals()[key] = element.text 
     # For example, if the key is "id", then it sets "self.id" to the text of the element in XML tag "<objectId>" 

в принципе, я хочу быть ленивым когда дело доходит до построения объектов ... Я не хочу набирать нагрузки «if xml ....». Это станет намного более важным, когда я начну работать с более сложными объектами, возвращаемыми API, с множеством элементов, с которыми нужно иметь дело.

Мой основной вопрос: что это лучший способ сделать это?

Cheers!

Мое решение:

Огромное спасибо Larsks за ответ. Привет, друг!

class scope: 
    xmlPathMap = { 
     "id" : "objectId", 
     "name" : "name", 
     "type" : "type/typeName" 
    } 
    def __init__(self, element): 
     self.element = element 
     self.create() 
    def create(self): 
     try: 
      for key, path in self.xmlPathMap.items(): 
       res = self.element.find(path) 
       if res is not None: 
        setattr(self, key, res.text) 
     except: 
      print "Error constructing scope object:\n\tkey: %s\n\tpath: %s\n\tvalue: %s" % (key, path, res.text) 
      return False 
# End of scope class definition 
+0

Почему вы называете 'o.create (et.tostring (объект))', а затем в 'create' немедленного вызова' XML = et.fromstring (xmlString) '? Почему бы просто не передать «объект» без сериализации/десериализации? – larsks

ответ

0

Как насчет этого? Это в основном то, что вы делаете в своем примере «Я бы хотел сделать что-то вроде ...». Атрибуты на объектах scope по умолчанию равны None, а затем устанавливаются с использованием словаря атрибута/xpath xmlPaths. В коде есть кое-что, о чем я не уверен (например, я не уверен, что вы делаете с locals() там), но я думаю, что это делает то, что вы хотите.

from lxml import etree 


class scope(object): 
    xmlPaths = {'id': 'objectId', 
       'name': 'name', 
       'type': 'type/typeName'} 

    def __init__(self, element): 
     self.id = None 
     self.name = None 
     self.type = None 
     self.element = element 

     self.parse() 

    def parse(self): 
     for attr, path in self.xmlPaths.items(): 
      res = self.element.xpath(path) 
      if res: 
       setattr(self, attr, res[0].text) 

    def __repr__(self): 
     return '<scope id=%s name=%s type=%s>' % (
      self.id, self.name, self.type) 


    with open('data.xml') as fd: 
     doc = etree.parse(fd) 

    scopeObjects = [] 
    for object in doc.iter('object'): 
     o = scope(object) 
     scopeObjects.append(o) 

    print scopeObjects 
+0

Приветствие! Функция setattr была именно тем, что я искал. Я отредактирую свой вопрос с помощью решения, которое я адаптировал из вашего примера. – Rowshi