从POST JSON数据初始化类

时间:2018-04-28 15:11:46

标签: python json django class deserialization

我正在编写一个Django应用程序,它将从站点发送一些数据到python脚本进行处理。我打算将这些数据作为JSON字符串发送(不一定是这种情况)。发送的一些值理想情况下是类实例,但这显然是不可能的,并且类名称加上初始化类所需的任何参数必须在将被python脚本反序列化之前如何序列化为JSON值。这可以通过下面的代码实现,但它有几个问题:

我的尝试

我已将每个类所需的所有数据放在一个列表中并用它来初始化每个类:

import json


class Class1():

    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)
        self._others = args


class Bar():

    POTENTIAL_OBJECTS = {"RANGE": range,
                         "Class1": Class1}

    def __init__(self, json_string):
        python_dict = json.loads(json_string)
        for key, value in python_dict.items():
            if isinstance(value, list) and value[0] in Bar.POTENTIAL_OBJECTS:
                setattr(self, key, Bar.POTENTIAL_OBJECTS[value[0]](*value[1], **value[2]))
            else:
                setattr(self, key, value)

example = ('{ "key_1":"Some string", "key_2":["heres", "a", "list"],'
           '"key_3":["RANGE", [10], {}], "key_4":["Class1", ["stuff"], {"stuff2":"x"}] }')

a = Bar(example)

我的方法存在的问题

除了一般有点凌乱而且不是特别优雅之外,还有其他问题。 JSON对象中的某些列表将由用户生成,如果用户使用POTENTIAL_OBJECTS中的密钥,这显然会出现问题。 (在非简化版本中,Bar会有很多子类,每个子类都有一个POTENTIAL_OBJECTS,因此跟踪前端验证的所有潜在值会很棘手。)

我的问题

感觉这必须是一个相当普遍的事情,并且必须有一些标准的模式或方法来实现这一点。是否有共同/更好的方法/方法来实现这一目标?

编辑:我已经意识到,问题的一个方法是让POTENTIAL_OBJECTS中的所有键都以下划线开头,然后验证用户输入中的任何下划线。前端。看起来似乎必须有更好的方法从JSON反序列化到比字符串/整数/布尔/列表等更复杂的对象。

1 个答案:

答案 0 :(得分:0)

除了使用一个主方法将任意JSON转换为Python对象的任意层次结构外,典型的模式是为您尝试建模的每种类型的事物创建一个Django模型。然后,它们之间的关系将通过relationship fields(ForeignKey,ManyToMany等,视情况而定)建模。例如,您可以创建一个为员工建模的类Employee和一个类Paycheck。然后,Paycheck可以有一个名为ForeignKey的{​​{1}}字段,引用issued_to

另请注意,任何与您描述的方案类似的方案(用户创建的JSON直接转换为任意Python对象)都会产生安全隐患,可能允许用户在Django服务器的上下文中执行任意代码,但是你要尝试它,白名单方法已经开始在这里开始作为一种安全的方式。

简而言之,您重新发明了Django已经为您做的大部分工作。 Django ORM功能将帮助您创建您感兴趣的特定事物的模型,验证数据,安全地将这些数据转换为Python对象,甚至将这些模型的实例保存在数据库中以便以后检索。

也就是说,如果要将JSON字符串直接解析为对象层次结构,则必须执行完全遍历而不是仅仅遍历顶级项目。为此,您应该考虑执行类似depth-first traversal的操作,在层次结构中的每个新节点上创建新的模型实例。如果要在服务器端验证这些输入,您还需要在Javascript中复制此工作。