【问题标题】:Deserializing a JSON string to a class instance in Haxe将 JSON 字符串反序列化为 Haxe 中的类实例
【发布时间】:2012-06-17 19:55:02
【问题描述】:

我正在尝试将 JSON 字符串反序列化为 Haxe 中的类实例。

class Action
{
    public var id:Int;
    public var name:String;

    public function new(id:Int, name:String)
    {
        this.id = id;
        this.name = name;
    }
}

我想做这样的事情:

var action:Action = haxe.Json.parse(actionJson);
trace(action.name);

但是,这会产生错误:

TypeError: Error #1034: Type Coercion failed: cannot convert Object@3431809 to Action

【问题讨论】:

    标签: json deserialization haxe


    【解决方案1】:

    Json 没有映射语言特定数据类型的机制,仅支持 JS 中包含的数据类型的子集。要保留有关 Haxe 类型的信息,您当然可以构建自己的机制。

    // This works only for basic class instances but you can extend it to work with 
    // any type.
    // It doesn't work with nested class instances; you can detect the required
    // types with macros (will fail for interfaces or extended classes) or keep
    // track of the types in the serialized object.
    // Also you will have problems with objects that have circular references.
    
    class JsonType {
      public static function encode(o : Dynamic) {
        // to solve some of the issues above you should iterate on all the fields,
        // check for a non-compatible Json type and build a structure like the
        // following before serializing
        return haxe.Json.stringify({
          type : Type.getClassName(Type.getClass(o)),
          data : o
        });
      }
    
      public static function decode<T>(s : String) : T {
        var o = haxe.Json.parse(s),
            inst = Type.createEmptyInstance(Type.resolveClass(o.type));
        populate(inst, o.data);
        return inst;
      }
    
      static function populate(inst, data) {
        for(field in Reflect.fields(data)) {
          Reflect.setField(inst, field, Reflect.field(data, field));
        }
      }
    }
    

    【讨论】:

    【解决方案2】:

    我扩展了 Franco's answer 以允许在您的 json 对象中递归地包含对象,只要在该对象上设置了 _explicitType 属性。

    比如下面的json:

    {
       intPropertyExample : 5,
       stringPropertyExample : 'my string',
       pointPropertyExample : {
          _explicitType : 'flash.geom.Point',
          x : 5,
          y : 6
       }
    }
    

    将正确地序列化为类如下所示的对象:

    import flash.geom.Point;
    
    class MyTestClass
    {
       public var intPropertyExample:Int;
       public var stringPropertyExample:String;
       public var pointPropertyExample:Point;
    }
    

    调用时:

    var serializedObject:MyTestClass = EXTJsonSerialization.decode([string of json above], MyTestClass)
    

    这是代码(注意它使用TJSON作为解析器,推荐CrazySam):

    import tjson.TJSON;
    
    class EXTJsonSerialization
    {
        public static function encode(o : Dynamic) 
        {
            return TJSON.encode(o);
        }
    
        public static function decode<T>(s : String, typeClass : Class<Dynamic>) : T 
        {
            var o = TJSON.parse(s);
            var inst = Type.createEmptyInstance(typeClass);
            EXTJsonSerialization.populate(inst, o);
            return inst;
        }
    
        private static function populate(inst, data) 
        {
            for (field in Reflect.fields(data)) 
            {
                if (field == "_explicitType")
                    continue;
    
                var value = Reflect.field(data, field);
                var valueType = Type.getClass(value);
                var valueTypeString:String = Type.getClassName(valueType);
                var isValueObject:Bool = Reflect.isObject(value) && valueTypeString != "String";
                var valueExplicitType:String = null;
    
                if (isValueObject)
                {
                    valueExplicitType = Reflect.field(value, "_explicitType");
                    if (valueExplicitType == null && valueTypeString == "Array")
                        valueExplicitType = "Array";
                }
    
                if (valueExplicitType != null)
                {
                    var fieldInst = Type.createEmptyInstance(Type.resolveClass(valueExplicitType));
                    populate(fieldInst, value);
                    Reflect.setField(inst, field, fieldInst);
                }
                else
                {
                    Reflect.setField(inst, field, value);
                }
            }
        }
    }
    

    【讨论】:

    • 请注意,上面的代码适用于 Flash 目标,但对于原生目标,您需要做一些更棘手的事情来处理数组。 this gist 中的代码应该可以处理这种情况。
    【解决方案3】:

    一个现代的、基于宏的库是json2object。可以这样使用:

    var parser = new json2object.JsonParser<Action>();
    var action:Action = parser.fromJson('{"id": 0, "name": "run"}', "action.json");
    

    另一个同样由宏驱动的选项是tink_json。在这种情况下,它有点冗长,因为它要求您使用 @:jsonParse metadata 指定应该如何解析一个类:

    @:jsonParse(function(json) return new Action(json.id, json.name))
    class Action {
    // ...
    

    解析是单行的:

    var action:Action = tink.Json.parse('{"id": 0, "name": "run"}');
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多