不使用Newtonsoft json但使用System.Web.Script.Serialization.JavaScriptSerializer反序列化具有不同数据类型的json字段 [英] Deserialize a json field with different data types without using Newtonsoft json but with System.Web.Script.Serialization.JavaScriptSerializer

查看:130
本文介绍了不使用Newtonsoft json但使用System.Web.Script.Serialization.JavaScriptSerializer反序列化具有不同数据类型的json字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在反序列化可以同时具有浮点数或数组类型数据的json数据时遇到问题。来自这里的相同问题
交易JSON字段在C#中保存不同的类型

I have an issue while deserializing json data which can have both float or array type of data. The same issue from here Dealing with JSON field that holds different types in C#

但是到处都可以使用json.net help / html / CustomJsonConverter.htm rel = nofollow noreferrer> JsonConverter 。我只需要在c#中使用 System.Web.Script.Serialization.JavaScriptSerializer 来实现反序列化。有人可以帮忙吗?

But everywhere the solution is to use json.net with a JsonConverter. I need to achieve the deserialization using only System.Web.Script.Serialization.JavaScriptSerializer in c#. Can anyone help, pls?

推荐答案

您可以使用 JavaScriptConverter 以此目的。但是,与Json.NET的 JsonConverter JavaScriptConverter 仅可用于与JSON 对象进行映射的类型,而不能用于数组或原始类型。因此,您将需要为可能包含数组或单例项目的多态属性的任何对象创建一个自定义转换器。

You can use a JavaScriptConverter for this purpose. However, unlike Json.NET's JsonConverter a JavaScriptConverter can only be used for types that map from and to a JSON object -- not an array or primitive type. Thus you will need to create a custom converter for any object that may contain a polymorphic property that could be an array or singleton item.

让我们假设您有一个看起来像JSON的JSON。以下内容:

Let's imagine you have JSON that looks like the following:

{
  "name": "my name",
  "data": {
    "foo": "Foo",
    "bar": "Bar"
  },
  "values": [
    3.14,
    2.718
  ]
}

其中有时可能是原始值,如下所示:

Where "values" might sometimes be a primitive value like so:

  "values": 3.14

而且,您想将此映射到以下POCO:

And, you want to map this to the following POCO:

public class RootObject
{
    public string name { get; set; }
    public NestedData data { get; set; }
    public float[] Values { get; set; }
}

public class NestedData
{
    public string foo { get; set; }
    public string bar { get; set; }
}

JavaScriptConverter.Deserialize() 传递了解析值的 IDictionary< string,object> ,采取的步骤是:


  1. 分离所有需要自定义处理的属性(请记住, JavaScriptSerializer 不区分大小写,但字典不区分大小写)。

  1. Detach any properties that need custom processing (keeping in mind that JavaScriptSerializer is case-insensitive but that the dictionary is not).

使用 JavaScriptSerializer.ConvertToType< T>() ,它使用的不是

Generate a default deserialization for any remaining properties using JavaScriptSerializer.ConvertToType<T>() using a fresh serializer that does not contain the converter.

手动反序列化并将自定义属性填充到部分反序列化的对象中,然后将其返回。

Manually deserialize and populate the custom properties into the partially deserialized object, and return it.

对于上面显示的类型,以下转换器基于此答案,可以完成以下工作:

For the type shown above, the following converter, based somewhat on this answer, does the job:

class RootObjectConverter : CustomPropertiesConverter<RootObject>
{
    const string ValuesName = "values";

    protected override IEnumerable<string> CustomProperties
    {
        get { return new[] { ValuesName }; }
    }

    protected override void DeserializeCustomProperties(Dictionary<string, object> customDictionary, RootObject obj, JavaScriptSerializer serializer)
    {
        object itemCost;
        if (customDictionary.TryGetValue(ValuesName, out itemCost) && itemCost != null)
            obj.Values = serializer.FromSingleOrArray<float>(itemCost).ToArray();
    }

    protected override void SerializeCustomProperties(RootObject obj, Dictionary<string, object> dict, JavaScriptSerializer serializer)
    {
        obj.Values.ToSingleOrArray(dict, ValuesName);
    }
}

public abstract class CustomPropertiesConverter<T> : JavaScriptConverter
{
    protected abstract IEnumerable<string> CustomProperties { get; }

    protected abstract void DeserializeCustomProperties(Dictionary<string, object> customDictionary, T obj, JavaScriptSerializer serializer);

    protected abstract void SerializeCustomProperties(T obj, Dictionary<string, object> dict, JavaScriptSerializer serializer);

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        // Detach custom properties
        var customDictionary = new Dictionary<string, object>();
        foreach (var key in CustomProperties)
        {
            object value;
            if (dictionary.TryRemoveInvariant(key, out value))
                customDictionary.Add(key, value);
        }

        // Deserialize and populate all members other than "values"
        var obj = new JavaScriptSerializer().ConvertToType<T>(dictionary);

        // Populate custom properties
        DeserializeCustomProperties(customDictionary, obj, serializer);

        return obj;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        // Generate a default serialization.  Is there an easier way to do this?
        var defaultSerializer = new JavaScriptSerializer();
        var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));

        // Remove default serializations of custom properties, if present
        foreach (var key in CustomProperties)
        {
            dict.RemoveInvariant(key);
        }

        // Add custom properties
        SerializeCustomProperties((T)obj, dict, serializer);

        return dict;
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get { return new[] { typeof(T) }; }
    }
}

public static class JavaScriptSerializerObjectExtensions
{
    public static void ReplaceInvariant<T>(this IDictionary<string, T> dictionary, string key, T value)
    {
        RemoveInvariant(dictionary, key);
        dictionary.Add(key, value);
    }

    public static bool TryRemoveInvariant<T>(this IDictionary<string, T> dictionary, string key, out T value)
    {
        if (dictionary == null)
            throw new ArgumentNullException();
        var keys = dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray();
        if (keys.Length == 0)
        {
            value = default(T);
            return false;
        }
        else if (keys.Length == 1)
        {
            value = dictionary[keys[0]];
            dictionary.Remove(keys[0]);
            return true;
        }
        else
        {
            throw new ArgumentException(string.Format("Duplicate keys found: {0}", String.Join(",", keys)));
        }
    }

    public static void RemoveInvariant<T>(this IDictionary<string, T> dictionary, string key)
    {
        if (dictionary == null)
            throw new ArgumentNullException();
        foreach (var actualKey in dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray())
            dictionary.Remove(actualKey);
    }

    public static void ToSingleOrArray<T>(this ICollection<T> list, IDictionary<string, object> dictionary, string key)
    {
        if (dictionary == null)
            throw new ArgumentNullException();
        if (list == null || list.Count == 0)
            dictionary.RemoveInvariant(key);
        else if (list.Count == 1)
            dictionary.ReplaceInvariant(key, list.First());
        else
            dictionary.ReplaceInvariant(key, list.ToArray());
    }

    public static List<T> FromSingleOrArray<T>(this JavaScriptSerializer serializer, object value)
    {
        if (value == null)
            return null;
        if (value.IsJsonArray())
        {
            return value.AsJsonArray().Select(i => serializer.ConvertToType<T>(i)).ToList();
        }
        else
        {
            return new List<T> { serializer.ConvertToType<T>(value) };
        }
    }

    public static bool IsJsonArray(this object obj)
    {
        if (obj is string || obj is IDictionary)
            return false;
        return obj is IEnumerable;
    }

    public static IEnumerable<object> AsJsonArray(this object obj)
    {
        return (obj as IEnumerable).Cast<object>();
    }
}

然后使用它:

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new RootObjectConverter() });
var root = serializer.Deserialize<RootObject>(json);

这篇关于不使用Newtonsoft json但使用System.Web.Script.Serialization.JavaScriptSerializer反序列化具有不同数据类型的json字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆