使用 Python 创建 JSON

Posted

技术标签:

【中文标题】使用 Python 创建 JSON【英文标题】:Use Python for Creating JSON 【发布时间】:2017-04-15 20:51:46 【问题描述】:

我想使用 Python 来创建 JSON。

由于我没有找到可以帮助我的库,我想知道是否可以检查 Python 文件中类的顺序?

例子

# example.py
class Foo:
    pass

class Bar:
    pass

如果我导入example,我想知道类的顺序。在这种情况下,它是 [Foo, Bar] 而不是 [Bar, Foo]。

这可能吗?如果“是”,怎么做?

背景

我对 yaml/json 不满意。我有一个模糊的想法是通过 Python 类创建配置(只有类,而不是对象的实例化)。

欢迎回答帮助我实现目标(使用简单有趣的工具创建 JSON)。

【问题讨论】:

这是一个非常模糊的想法。你应该给Figura一个机会! 我应该指出声明的顺序没有保留在 Figura 和 also not in json 中,也没有保留在 YAML 中。 @shx2 AFAIK yaml 中的订单被保留。否则 saltstack 中的排序状态将不起作用:docs.saltstack.com/en/latest/ref/states/ordering.html @shx2 看了Figura的教程。很难阅读这些示例,因为我的眼睛大多看到"""。需要 cmets 吗? 好的,我会考虑让它看起来更容易;)但是,您并不真的需要文档。入门真的很容易。查看 hello_world.py 【参考方案1】:

inspect 模块可以告诉类声明的行号:

import inspect

def get_classes(module):
    for name, value in inspect.getmembers(module):
        if inspect.isclass(value):
            _, line = inspect.getsourcelines(value)
            yield line, name

所以下面的代码:

import example

for line, name in sorted(get_classes(example)):
    print line, name

打印:

2 Foo
5 Bar

【讨论】:

这要求示例模块位于导入路径上,这也是一个很大的安全漏洞,您正在与应用程序的其余部分相同的上下文中加载用户可编辑的文件,没有任何一种沙盒...... @pradyunsg:他写道“如果我导入示例”,因此导入似乎没有问题。 哦...不过,我认为这是一个安全漏洞。在我的答案中添加了一个部分。 是的,可以,具体取决于用例。 当然,取决于用例。如果地球上的随机人无法访问它,那可能没问题。【参考方案2】:

首先,在我看来,你可以做两件事...

    继续追求使用Python源文件作为配置文件。 (我不建议这样做。这类似于用推土机敲钉子或将a shotgun 转换为***)

    切换到 TOML、JSON 或 YAML 之类的配置文件,这些文件专为工作而设计。

    JSON 或 YAML 中没有任何内容可以阻止它们持有“有序”的键值对。 Python 的 dict 数据类型默认是无序的(至少在 3.5 之前),list 数据类型是有序的。当使用默认加载器时,它们分别直接映射到 JSON 中的对象和数组。只需在反序列化它们时使用 Python 的 OrderedDict 之类的东西,瞧,你可以保持秩序!


除此之外,如果您真的想使用 Python 源文件进行配置,我建议尝试使用 ast 模块处理文件。抽象语法树是语法级别分析的强大工具。

我编写了一个快速脚本,用于从文件中提取类行号和名称。

您(或任何人)可以使用它或将其扩展为更广泛,并且如果您想要任何您想要的东西,可以进行更多检查。

import sys
import ast
import json


class ClassNodeVisitor(ast.NodeVisitor):

    def __init__(self):
        super(ClassNodeVisitor, self).__init__()
        self.class_defs = []

    def visit(self, node):
        super(ClassNodeVisitor, self).visit(node)
        return self.class_defs

    def visit_ClassDef(self, node):
        self.class_defs.append(node)


def read_file(fpath):
    with open(fpath) as f:
        return f.read()


def get_classes_from_text(text):
    try:
        tree = ast.parse(text)
    except Exception as e:
        raise e

    class_extractor = ClassNodeVisitor()

    li = []
    for definition in class_extractor.visit(tree):
        li.append([definition.lineno, definition.name])

    return li


def main():
    fpath = "/tmp/input_file.py"

    try:
        text = read_file(fpath)
    except Exception as e:
        print("Could not load file due to " + repr(e))
        return 1

    print(json.dumps(get_classes_from_text(text), indent=4))


if __name__ == '__main__':
    sys.exit(main())

这是对以下文件的示例运行:

input_file.py:

class Foo:
    pass


class Bar:
    pass

输出:

$ py_to_json.py input_file.py
[
    [
        1,
        "Foo"
    ],
    [
        5,
        "Bar"
    ]
]

如果我导入example

如果您要导入模块,example 模块将位于导入路径上。导入意味着在example 模块中执行任何 Python 代码。这是一个相当大的安全漏洞——您在与应用程序的其余部分相同的上下文中加载用户可编辑的文件。

【讨论】:

ast 模块非常好,感谢您的示例。我一直在玩它,直到达到单线:[(n.lineno, n.name) for n in ast.walk(ast.parse(open("example.py").read())) if isinstance(n, ast.ClassDef)] 是的。这也有效。我觉得很傻。我最好的回归是:我做了这么多,因为我不确定 OP 是否只需要类名或其中的作业。 (因为我也为此编写了代码,然后将其删除) ast.NodeVisitorast.walk 都在树上行走。 ast.walk 对于这种单行来说更简单,但ast.NodeVisitor 对于更复杂的处理可能更优雅。我只是更喜欢简短的例子,因为它更容易让读者理解。 同意。但较短的示例并非总是更容易,但往往更容易。 @NorbertSebők 如果你导入了example.py,你可以使用"".join(inspect.getsourcelines(example)[0])而不是open("example.py").read()【参考方案3】:

我假设既然您关心保留类定义顺序,那么您也关心保留每个类中的定义顺序

值得指出的是,现在python中的默认行为,since python3.6。

参见PEP 520:保留类属性定义顺序

【讨论】:

这是个好消息。到目前为止,我们仍然使用 Python 2.7,但迟早我们会切换。谢谢。【参考方案4】:

(将我的 cmets 移动到答案)

这是一个非常模糊的想法。你应该试一试Figura!它就是这么做的。

(完全披露:我是 Figura 的作者。)

我应该指出声明的顺序没有保留在 Figura 中,也没有保留在 json 中。

我不确定 YAML 中的订单保留,但我确实在 wikipedia 上找到了这个:

...根据规范,映射键没有顺序

可能是特定的 YAML 解析器维护顺序,尽管它们不是必需的。

【讨论】:

【参考方案5】:

您可以使用元类来记录每个类的创建时间,然后按它对类进行排序。

这适用于python2:

class CreationTimeMetaClass(type): 
    creation_index = 0
    def __new__(cls, clsname, bases, dct):
        dct['__creation_index__'] = cls.creation_index
        cls.creation_index += 1
        return type.__new__(cls, clsname, bases, dct)

__metaclass__ = CreationTimeMetaClass

class Foo: pass
class Bar: pass

classes = [ cls for cls in globals().values() if hasattr(cls, '__creation_index__') ]
print(sorted(classes, key = lambda cls: cls.__creation_index__))

【讨论】:

【参考方案6】:

标准的json 模块易于使用,非常适合读写 JSON 配置文件。

对象在 JSON 结构中没有排序,但列表/数组是排序的,因此将依赖于顺序的信息放入列表中。

我使用类作为配置工具,我所做的事情是从由特定类变量自定义的基类派生它们。通过使用这样的类,我不需要工厂类。例如:

from .artifact import Application
class TempLogger(Application): partno='03459'; path='c:/apps/templog.exe'; flag=True
class GUIDisplay(Application): partno='03821'; path='c:/apps/displayer.exe'; flag=False

在安装脚本中

from .install import Installer
import app_configs

installer = Installer(apps=(TempLogger(), GUIDisplay()))
installer.baseline('1.4.3.3475')
print installer.versions()
print installer.bill_of_materials()

应该使用正确的工具来完成这项工作,因此如果您需要订购,python 类可能不是正确的工具。

我用来创建 JSON 文件的另一个 python 工具是Mako 模板系统。这是非常强大的。我们使用它将 IP 地址等变量填充到静态 JSON 文件中,然后由 C++ 程序读取。

【讨论】:

【参考方案7】:

我不确定这是否能回答您的问题,但它可能是相关的。看看优秀的attrs 模块。它非常适合创建用作数据类型的类。

这是来自glyph's 博客(Twisted Python 的创建者)的示例:

import attr
@attr.s
class Point3D(object):
    x = attr.ib()
    y = attr.ib()
    z = attr.ib()

它可以节省您编写大量样板代码 - 您可以免费获得 str 表示和比较之类的东西,并且该模块具有方便的 asdict 函数,您可以将其传递给 json 库:

>>> p = Point3D(1, 2, 3)
>>> str(p)
'Point3D(x=1, y=2, z=3)'
>>> p == Point3D(1, 2, 3)
True
>>> json.dumps(attr.asdict(p))
'"y": 2, "x": 1, "z": 3'

该模块使用了一种奇怪的命名约定,但将attr.s 读作“attrs”,将attr.ib 读作“attrib”,就可以了。

【讨论】:

【参考方案8】:

刚刚触及从 python 创建 JSON 的要点。有一个很棒的库叫做jsonpickle,它可以让你将python对象转储到json。 (单独使用它或与此处提到的其他方法一起使用,您可能会得到您想要的)

【讨论】:

它也适用于课堂吗?我没有对象。 它可以用于课程,但这真的取决于您的需求。从我所看到的它将编码为:“py/object”:“.B”,然后它可以对其进行解码以重建 B 它模块是可访问的

以上是关于使用 Python 创建 JSON的主要内容,如果未能解决你的问题,请参考以下文章

漂亮的打印 JSON python

python中的JSON

平面 json 到嵌套 json python

python与json的数据转换

InstagramAPI - 如何将 JSON Python 数据解析为 Pandas DataFrame(iPython、Jupyter Notebook)

如何将有效json中的字符串json转换为有效的json python