Сериализация объекта Python в JSON, созданного с помощью eulxml.xmlmap.XmlObject

У меня есть набор объектов Python, которые я создаю с помощью eulxml.xmlmap.XmlObject (я использую этот метод в первую очередь потому, что работаю с сервером eXistDB, а eulxml предлагает довольно простую функцию сопоставления). Я могу успешно запросить свою eXistDB и загрузить набор результатов xquery в некоторые созданные мной объекты python. Моя проблема в том, что я хочу иметь возможность записывать эти объекты как JSON, когда я передаю их на веб-сервер (используя Angular для внешнего интерфейса).

Я пытался использовать jsonpickle, но кажется, что eulxml выполняет какую-то магию ленивой загрузки. Стандартный вызов jsonpickle для сериализации моего объекта в json дает мне такой результат:

код питона:

jsonpickle.encode(myObject)

результат:

"py/object": "models.alcalaPage.AlcalaPage", "context": 
{"namespaces": 
   {"exist": "http://exist.sourceforge.net/NS/exist"}
}, 
"node":  {
   "py/object": "lxml.etree._Element", 
   "py/seq": [
       {"py/object": "lxml.etree._Comment", "py/seq": []},
       {"py/object": "lxml.etree._Element", "py/seq": []},
       ...
    ]
   }... 

кажется, что он выводит только тип атрибута, но не значение самого атрибута. Если я изменю свой код jsonpickle, чтобы установить unpickable=False, все, что я получу, это пустой набор json (это означает, что структура существует с точки зрения правильного количества фигурных скобок и скобок, но буквально нет данных. Вывод json: только фигурные скобки и скобки).

Я подумал, что, возможно, если я попытаюсь получить доступ к значению в поле, а затем выведу json, это может сработать (по крайней мере, для поля, к которому я обращался), но не повезло. Я получаю тот же результат, что и выше (и да, я дважды проверил, что в самом объекте есть данные).

Я как бы в растерянности в этот момент. Я мог бы перейти на что-то вроде BeautifulSoup, но это требует написания НАМНОГО большего количества кода (eulxml позволяет мне просто указать xpath к значению, которым я хочу заполнить свой атрибут, и bing, я готов). Есть ли что-то, что мне не хватает с jsonpickle? Или есть другой пакет json, на который я должен посмотреть? Или, может быть, я делаю этот способ более сложным, чем мне нужно, и есть другой способ запросить eXistDB с помощью python, а затем отправить информацию во внешнее приложение, созданное с использованием Angular. Я открыт для предложений.

Я приведу примеры своего кода ниже (я не буду включать все это, потому что, вероятно, я работаю с 10+ объектами):

Пример кода объекта с eulxml:

import jsonpickle
from eulxml.xmlmap import XmlObject


class AlcalaBase(XmlObject):

    def to_xml(self):
        return self.serializeDocument(pretty=True)

    def to_json(self):
        return jsonpickle.encode(self)

from eulxml import xmlmap
from models.alcalaBase import AlcalaBase

class AlcalaPage(AlcalaBase):
    ROOT_NAME = 'page'
    id = xmlmap.StringField('pageID')
    archive_page_number = xmlmap.StringField('archivistsPageNumber')
    year = xmlmap.IntegerField('content/@yearID')

person Shane McGarry    schedule 22.04.2018    source источник


Ответы (2)


Я смог выяснить проблему (вроде). Поэтому я отправляю это здесь, если у других есть такая же проблема.

Проблема заключается в том, что атрибуты не добавляются в dict, поэтому фактические значения не выводятся во время процесса json. Я написал свой метод to_json() в своем базовом классе, чтобы выводить соответствующие объекты. ПРИМЕЧАНИЕ. Хотя я пытался сделать это как можно более общим, это несколько специфично для моей структуры данных (в том смысле, что я знаю, чего ожидать в данных сценариях, и поскольку я имею дело со статическими данными, мне не нужно «будущее»). доказательство» решения. Любой, кто примет этот код, должен адаптировать его к своему сценарию.

from eulxml import xmlmap
import inspect
import lxml
import json as JSON

class AlcalaBase(xmlmap.XmlObject):

    def to_json(self, skipBegin=False):
        json = list()
        if not skipBegin:
            json.append('{')
        json.append(str.format('"{0}": {{', self.ROOT_NAME))
        for attr, value in inspect.getmembers(self):
            if (attr.find("_") == -1
                and attr.find("serialize") == -1
                and attr.find("context") == -1
                and attr.find("node") == -1
                and attr.find("schema") == -1):
                if type(value) is xmlmap.fields.NodeList:
                    if len(value) > 0:
                        json.append(str.format('"{0}": [', attr))
                        for v in value:
                            json.append(v.to_json())
                            json.append(",")
                        json = json[:-1]
                        json.append("]")
                    else:
                        json.append(str.format('"{0}": null', attr))
                elif (type(value) is xmlmap.fields.StringField
                        or type(value) is str
                        or type(value) is lxml.etree._ElementUnicodeResult):
                        value = JSON.dumps(value)
                        json.append(str.format('"{0}": {1}', attr, value))
                elif (type(value) is xmlmap.fields.IntegerField
                    or type(value) is int
                    or type(value) is float):
                    json.append(str.format('"{0}": {1}', attr, value))
                elif value is None:
                    json.append(str.format('"{0}": null', attr))
                elif type(value) is list:
                    if len(value) > 0:
                        json.append(str.format('"{0}": [', attr))
                        for x in value:
                            json.append(x)
                            json.append(",")
                        json = json[:-1]
                        json.append("]")
                    else:
                        json.append(str.format('"{0}": null', attr))
                else:
                    json.append(value.to_json(skipBegin=True))
                json.append(",")
        json = json[:-1]
        if not skipBegin:
            json.append('}')
        json.append('}')
        return ''.join(json)

Все, что наследуется от этого класса, сможет сериализоваться в json. Это также предполагает, что все коллекции объектов наследуются от этого базового класса (в моей конкретной модели это верно, так что это не проблема).

person Shane McGarry    schedule 25.04.2018

Да, jsonpickle вызывает метод dict, чтобы он работал, вы можете использовать в метаклассе следующее:

class MyXmlObject(XmlObject):
    @property
    def __dict__(self):
        d = { 'ROOT_NAME': self.ROOT_NAME }
        for key, value in self._fields.items():
            if isinstance(value, fields.Field):
                if isinstance(value, fields.NodeListField):
                    d[key] = [x.__dict__ for x in getattr(self, key)]
                elif isinstance(value, fields.NodeField):
                    d[key] = getattr(self, key).__dict__
                else:
                    d[key] = getattr(self, key)
        return d

поэтому метод dict будет напрямую возвращать значения полей

person Affectus    schedule 22.10.2019