将 Python 对象序列化为 JSON 创建于 eulxml.xmlmap.XmlObject

Serializing Python Object to JSON Created with eulxml.xmlmap.XmlObject

我有一组 python 使用 eulxml.xmlmap.XmlObject 创建的对象(我使用此方法主要是因为我使用的是 eXistDB 服务器,而 eulxml 提供了非常简单的映射功能)。我能够成功查询我的 eXistDB 并将 xquery 结果集加载到我创建的一些 python 对象中。我的问题是,当我将这些对象传递给网络服务器时,我希望能够将这些对象写为 JSON(前端使用 Angular)。

我试过使用 jsonpickle,但似乎 eulxml 正在执行某种延迟加载魔法。对 jsonpickle 的标准调用将我的对象序列化为 json 得到了这个结果:

python代码:

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 包吗?或者也许我使这种方式变得比我需要的更困难,并且有一些其他方法可以使用 python 查询 eXistDB,然后将信息发送到使用 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')

我能够(某种程度上)找出问题所在。所以我把它贴在这里以防其他人有同样的问题。

问题似乎是属性没有添加到 dict 所以在 json 过程中没有输出实际值。我在基 class 中编写了 to_json() 方法,以便输出适当的对象。注意:虽然我尽量保持它的通用性,但它在某种程度上特定于我的数据结构(因为我知道在给定场景中会发生什么,并且由于我正在处理静态数据,所以我不必 "future proof" 解决方案。任何采用此代码的人都应该根据他们给定的场景调整它。

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)

继承自此 class 的任何内容都可以序列化为 json。这还假设所有对象集合都继承自该基础 class(在我的特定模型中,这是真的,所以这不是问题)。

是的,jsonpickle 调用 dict 方法使其工作,您可以在 meta class:

中使用以下内容
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 方法将直接 return 字段的值