使用不同的数据类型反序列化json字段,而不使用Newtonsoft json但使用System.Web.Script.Serialization.JavaScriptSerializer

时间:2017-06-08 11:28:31

标签: c# json deserialization javascriptserializer

在反序列化json数据时遇到问题,这些数据可以包含float或数组类型的数据。同样的问题来自这里 Dealing with JSON field that holds different types in C#

但无论何处,解决方案都是JsonConverter一起使用。我需要在c#中仅使用System.Web.Script.Serialization.JavaScriptSerializer来实现反序列化。任何人都可以帮忙吗?

1 个答案:

答案 0 :(得分:1)

您可以使用JavaScriptConverter来实现此目的。但是,与Json.NET的JsonConverter不同,JavaScriptConverter只能用于映射JSON 对象的类型 - 而不是数组或基本类型。因此,您需要为任何可能包含多态属性的对象创建自定义转换器,该属性可以是数组或单例项。

让我们假设您的JSON如下所示:

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

"values"有时可能是原始值,如下所示:

  "values": 3.14

并且,您希望将其映射到以下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不区分大小写,但字典不是)。

  2. 使用不包含转换器的新序列化程序,使用JavaScriptSerializer.ConvertToType<T>()为任何剩余属性生成默认反序列化。

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

  4. 对于上面显示的类型,以下转换器(部分基于this answer)完成了这项工作:

    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);