在反序列化json数据时遇到问题,这些数据可以包含float或数组类型的数据。同样的问题来自这里 Dealing with JSON field that holds different types in C#
但无论何处,解决方案都是json.net与JsonConverter
一起使用。我需要在c#中仅使用System.Web.Script.Serialization.JavaScriptSerializer
来实现反序列化。任何人都可以帮忙吗?
答案 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>
个已解析的值时,要采取的步骤为:
分离任何需要自定义处理的属性(请记住,JavaScriptSerializer
不区分大小写,但字典不是)。
使用不包含转换器的新序列化程序,使用JavaScriptSerializer.ConvertToType<T>()
为任何剩余属性生成默认反序列化。
手动反序列化并将自定义属性填充到部分反序列化的对象中,并将其返回。
对于上面显示的类型,以下转换器(部分基于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);