【问题标题】:Unable to deserialize my json无法反序列化我的 json
【发布时间】:2017-04-02 18:13:36
【问题描述】:

这是我的课

第一个实际上需要转换为好的 json 数据。 这是最初的坏人

{
    "channels": {
        "heart-rate": {
            "events": {
                "$type": "System.Collections.Generic.List`1[[Project.Model.Activity+Channel+Event, Project]], mscorlib",
                "$values": [{
                        "$type": "Project.Model.ChannelEvents.HeartRateChannelEvent, LTF.MyPlan.ActivityUtil",
                        "beatsPerMinute": 40,
                        "offset": 0
                    }
                ]
            }
        },
        "location": {
            "events": {
                "$type": "System.Collections.Generic.List`1[[Project.Model.Activity+Channel+Event, Project]], mscorlib",
                "$values": [{
                        "$type": "Project.Model.ChannelEvents.LocationChannelEvent, Project",
                        "latitude": 0.0,
                        "longitude": 0.0,
                        "offset": 0
                    }
                ]
            }
        }
    }
}

public class LocationChannelEvent : Activity.Channel.Event    
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public float? Distance { get; set; }
    public float? Altitude { get; set; }

    /// <summary>
    /// Speed in m/s
    /// </summary>
    public float? Speed { get; set; }

这是我无法反序列化的 json 数据。我一直收到默认值,即使我改变了

{


"location": {
            "events": {
                "$type": "System.Collections.Generic.List`1[[Project.Model.Activity+Channel+Event, Project]], mscorlib",
                "$values": [{
                        "$type": "Project.Model.ChannelEvents.LocationChannelEvent, Project",
                        "latitude": 0.0,
                        "longitude": 0.0,
                        "offset": 0
            ]
        }
    }
}

我的自定义封面

public class CompactListConverter : JsonConverter
    {
        public const string TypeKey = "type";
        public const string StructureKey = "structure";
        public const string ListKey = "list";

    /// <summary>
    /// Only convert lists of non-enumerable class types.
    /// </summary>
    /// <param name="objectType"></param>
    /// <returns></returns>
    public override bool CanConvert(Type objectType)
    {
        var objectTypeInfo = objectType.GetTypeInfo();

        if (objectTypeInfo.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>))
        {
            var itemTypeInfo = objectTypeInfo.GenericTypeArguments.Single().GetTypeInfo();
            if (itemTypeInfo.IsClass && !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(itemTypeInfo))
            {
                return true;
            }
        }
        return false;
    }

    /// <summary>
    ///  Generates a wrapper object containing type, structure, and the condensed list.
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="value"></param>
    /// <param name="serializer"></param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var list = (IList)value;
        if (list.Count > 0)
        {
            var array = new JArray();
            var wrapper = GetWrapper(list, serializer);

            foreach (var item in list)
            {
                var obj = JObject.FromObject(item, serializer);
                var itemValues = new JArray();
                foreach (var prop in obj.Properties())
                {
                    itemValues.Add(prop.Value);
                }
                array.Add(itemValues);
            }

            wrapper.Add(ListKey, array);
            wrapper.WriteTo(writer);
        }
        else
        {
            new JObject().WriteTo(writer);
        }
    }

    private JObject GetWrapper(IList list, JsonSerializer serializer)
    {
        var wrapper = new JObject {{TypeKey, list[0].GetType().AssemblyQualifiedName}};

        var keys = new JArray();

        var first = JObject.FromObject(list[0], serializer);
        foreach (var prop in first.Properties())
        {
            keys.Add(new JValue(prop.Name));
        }

        wrapper.Add(StructureKey, keys);

        return wrapper;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var wrapper = JObject.Load(reader);
        var itemType = Type.GetType(wrapper.GetValue(TypeKey).ToObject<string>());
        var array = wrapper.GetValue(ListKey) as JArray;

        var list = existingValue as IList ?? (IList) Activator.CreateInstance(typeof (List<>).MakeGenericType(new[] {itemType}));

        if (array != null && array.Count > 0)
        {
            var keys = wrapper.GetValue(StructureKey) as JArray ?? new JArray();
            foreach (var itemValues in array.Children<JArray>())
            {
                var item = new JObject();
                for (var i = 0; i < keys.Count; i++)
                {
                    item.Add(new JProperty(keys[i].ToString(), itemValues[i]));
                }

                list.Add(item.ToObject(itemType, serializer));
            }
        }
        return list;
    }
}


public class ChannelCompactingConverter : CompactListConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return base.CanConvert(objectType) 
                && typeof(IList<Activity.Channel.Event>).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
        }
    }

【问题讨论】:

  • 它没有理由起作用。使用例如json.net,如果您查看docs,您会发现如果启用TypeNameHandling,您的JSON 应该类似于original question 中的badjson。为什么不直接反序列化 badjson 而不进行转换?
  • 我到底在哪里使用这个。 typenamehandling-我应该创建一个新的反序列化类
  • 您是在问如何直接反序列化您上一个问题中的 badjson,而不进行转换?
  • 我想将它转换成太好的 json 并反序列化 - 因为我需要将它保存回 db
  • 你确定你不是在这里解决XY problem吗?你原来所谓的 badjson 可以很容易地反序列化,为什么要先转换呢?

标签: c# json deserialization


【解决方案1】:

只要您使用custom JsonConverer,您就可以使用 以所示格式反序列化和重新序列化List&lt;LocationChannelEvent&gt;。这是必需的,因为默认情况下,对象集合从 JSON 数组序列化和向 JSON 数组序列化,但在 JSON 中,对象集合以单个对象的稍微更紧凑的形式序列化,其中对象属性名称被序列化在名为"structure" 的字符串数组中只有一次,对象本身表示为值数组,内部数组与结构体数组一一对应。

因此,如果您创建以下转换器:

public class StructuredListConverter<T> : JsonConverter
{
    const string typeName = "type";
    const string structureName = "structure";
    const string listName = "list";

    public override bool CanConvert(Type objectType)
    {
        if (!typeof(ICollection<T>).IsAssignableFrom(objectType))
            return false;
        // This converter is only implemented for read/write collections.  So no arrays.
        if (objectType.IsArray)
            return false; 
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var collection = existingValue as ICollection<T> ?? (ICollection<T>) serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        var root = JObject.Load(reader);
        var structure = root[structureName] == null ? null : root[structureName].ToObject<string []>();
        if (structure == null)
            throw new JsonSerializationException("structure not found.");
        var listToken = root[listName];
        if (listToken == null || listToken.Type == JTokenType.Null)
            return collection;
        var list = listToken as JArray;
        if (list == null)
            throw new JsonSerializationException("list was not an array.");
        if (list == null || list.Count == 0)
            return collection;
        foreach (var item in list)
        {
            if (item == null || item.Type == JTokenType.Null)
                collection.Add(default(T));
            else if (item.Type != JTokenType.Array)
                throw new JsonSerializationException(string.Format("Item was not an array: {0}", item));
            else
                collection.Add(new JObject(item.Zip(structure, (i, n) => new JProperty(n, i))).ToObject<T>());
        }
        return collection;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
        if (contract == null)
            throw new JsonSerializationException(string.Format("Type {0} is not mapped to a JSON object.", typeof(T)));

        var collection = (ICollection<T>)value;
        writer.WriteStartObject();

        // Write item type
        writer.WritePropertyName(typeName);
        serializer.Serialize(writer, typeof(T));

        // Write structure (property names)
        var structure = contract.Properties.Where(p => p.Readable && !p.Ignored).Select(p => p.PropertyName).ToList();
        writer.WritePropertyName(structureName);
        serializer.Serialize(writer, structure);

        // Write array of array of values
        var query = collection
            .Select(i => i == null ? null : contract.Properties.Where(p => p.Readable && !p.Ignored).Select(p => p.ValueProvider.GetValue(i)));
        writer.WritePropertyName(listName);
        serializer.Serialize(writer, query);

        writer.WriteEndObject();
    }
}

并按如下方式定义您的数据模型:

public class LocationChannelEvent : Activity.Channel.Event
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public float? Distance { get; set; }
    public float? Altitude { get; set; }

    /// <summary>
    /// Speed in m/s
    /// </summary>
    public float? Speed { get; set; }
}

public class Location
{
    [JsonConverter(typeof(StructuredListConverter<LocationChannelEvent>))]
    public List<LocationChannelEvent> events { get; set; }
}

public class RootObject
{
    public Location location { get; set; }
}

您将能够反序列化和重新序列化显示的 JSON。

原型fiddle.

【讨论】:

  • 我们已经在 Utility 中内置了一个自定义转换器。让我将它添加到问题中
  • @kishore - 现在这是一个完全不同的问题,我上面的所有工作似乎都是在浪费时间。您将需要调试转换器以确定问题所在。如果您无法确定问题,请询问。见stackoverflow.com/help/how-to-ask
  • 麻烦您了
  • 我的转换器一切正常,但某些数据已损坏,就像我显示为错误 json 数据的数据一样。我需要以某种方式将其设置回良好的 json
【解决方案2】:

就我所熟悉的 json 而言,class 和 json 这两个东西是两个完全不同的东西!所以很明显不能这样序列化。

在这种情况下,我将 json 反序列化为 dynamic 对象,然后查看 C# 创建的对象并将其与我的类进行比较。你可以这样做:

dynamic res = JsonConvert.Deserialize(json);

然后你可以在这里放置一个断点,看看反序列化器给你什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 2021-05-14
    • 2019-08-06
    相关资源
    最近更新 更多