C# 使用引用将Json.NET反序列化为动态对象
如何让Json.NET反序列化为动态对象,但仍然执行引用解析?C# 使用引用将Json.NET反序列化为动态对象,c#,dynamic,json.net,C#,Dynamic,Json.net,如何让Json.NET反序列化为动态对象,但仍然执行引用解析? dynamic d=JsonConvert.DeserializeObject(…)就像 dynamic d=JsonConvert.DeserializeObject(…)返回一个动态对象,但它们不解析$ref和$id部分。(例如,ExpandoObject eo将只具有eo[“$ref”]=“…”,并且不具有它应该具有的属性,因为它与$id-对象不同) 我发现我需要契约解析程序解析为动态契约-只有当我使用自定义的契约解析程序显式
dynamic d=JsonConvert.DeserializeObject(…)
就像dynamic d=JsonConvert.DeserializeObject(…)
返回一个动态对象,但它们不解析$ref
和$id
部分。(例如,ExpandoObject eo
将只具有eo[“$ref”]=“…”
,并且不具有它应该具有的属性,因为它与$id
-对象不同)
我发现我需要契约解析程序解析为动态契约-只有当我使用自定义的契约解析程序显式地告诉Json.NET时,ExpandoObject
才会解析为动态契约
但是,ExpandoObject
似乎是用它自己的转换器解析的,它再次失败
我尝试了一个从IDynamicMetaObjectProvider
继承的自定义类,它导致了一个无限循环,似乎不是正确的选择。实际上,我希望一些简单的解决方案能够使ExpandoObject
具有参考分辨率
有什么帮助吗?由于Json.NET是开源的,并且是MIT许可证,最简单的解决方案可能是根据您的需要调整它:
/// <summary>
/// Converts an ExpandoObject to and from JSON, handling object references.
/// </summary>
public class ObjectReferenceExpandoObjectConverter : JsonConverter
{
// Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// can write is set to false
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return ReadValue(serializer, reader);
}
private object ReadValue(JsonSerializer serializer, JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment)
{
if (!reader.Read())
throw reader.CreateException("Unexpected end when reading ExpandoObject.");
}
switch (reader.TokenType)
{
case JsonToken.StartObject:
return ReadObject(serializer, reader);
case JsonToken.StartArray:
return ReadList(serializer, reader);
default:
if (JsonTokenUtils.IsPrimitiveToken(reader.TokenType))
return reader.Value;
throw reader.CreateException("Unexpected token when converting ExpandoObject");
}
}
private object ReadList(JsonSerializer serializer, JsonReader reader)
{
IList<object> list = new List<object>();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
default:
object v = ReadValue(serializer, reader);
list.Add(v);
break;
case JsonToken.EndArray:
return list;
}
}
throw reader.CreateException("Unexpected end when reading ExpandoObject.");
}
private object ReadObject(JsonSerializer serializer, JsonReader reader)
{
IDictionary<string, object> expandoObject = null;
object referenceObject = null;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
string propertyName = reader.Value.ToString();
if (!reader.Read())
throw new InvalidOperationException("Unexpected end when reading ExpandoObject.");
object v = ReadValue(serializer, reader);
if (propertyName == "$ref")
{
var id = (v == null ? null : Convert.ToString(v, CultureInfo.InvariantCulture));
referenceObject = serializer.ReferenceResolver.ResolveReference(serializer, id);
}
else if (propertyName == "$id")
{
var id = (v == null ? null : Convert.ToString(v, CultureInfo.InvariantCulture));
serializer.ReferenceResolver.AddReference(serializer, id, (expandoObject ?? (expandoObject = new ExpandoObject())));
}
else
{
(expandoObject ?? (expandoObject = new ExpandoObject()))[propertyName] = v;
}
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
if (referenceObject != null && expandoObject != null)
throw reader.CreateException("ExpandoObject contained both $ref and real data");
return referenceObject ?? expandoObject;
}
}
throw reader.CreateException("Unexpected end when reading ExpandoObject.");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ExpandoObject));
}
public override bool CanWrite
{
get { return false; }
}
}
public static class JsonTokenUtils
{
// Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs
public static bool IsPrimitiveToken(this JsonToken token)
{
switch (token)
{
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Undefined:
case JsonToken.Null:
case JsonToken.Date:
case JsonToken.Bytes:
return true;
default:
return false;
}
}
}
public static class JsonReaderExtensions
{
public static JsonSerializationException CreateException(this JsonReader reader, string format, params object[] args)
{
// Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonPosition.cs
var lineInfo = reader as IJsonLineInfo;
var path = (reader == null ? null : reader.Path);
var message = string.Format(CultureInfo.InvariantCulture, format, args);
if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
{
message = message.Trim();
if (!message.EndsWith(".", StringComparison.Ordinal))
message += ".";
message += " ";
}
message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path);
if (lineInfo != null && lineInfo.HasLineInfo())
message += string.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition);
message += ".";
return new JsonSerializationException(message);
}
}
//
///将ExpandooObject转换为JSON和JSON,处理对象引用。
///
公共类对象引用ExpandoobjectConverter:JsonConverter
{
//改编自https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
//can write设置为false
抛出新的NotImplementedException();
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
返回ReadValue(序列化程序、读取器);
}
私有对象ReadValue(JsonSerializer序列化程序、JsonReader阅读器)
{
while(reader.TokenType==JsonToken.Comment)
{
如果(!reader.Read())
抛出reader.CreateException(“读取ExpandooObject时意外结束”);
}
开关(reader.TokenType)
{
案例JsonToken.StartObject:
返回ReadObject(序列化程序、读取器);
案例JsonToken.StartArray:
返回ReadList(序列化程序、读取器);
违约:
if(JsonTokenUtils.IsPrimitiveToken(reader.TokenType))
返回reader.Value;
抛出reader.CreateException(“转换ExpandooObject时出现意外标记”);
}
}
私有对象读取列表(JsonSerializer序列化程序、JsonReader阅读器)
{
IList list=新列表();
while(reader.Read())
{
开关(reader.TokenType)
{
案例JsonToken。评论:
打破
违约:
对象v=读取值(序列化程序、读取器);
增加(五)项;
打破
案例JsonToken.EndArray:
退货清单;
}
}
抛出reader.CreateException(“读取ExpandooObject时意外结束”);
}
私有对象ReadObject(JsonSerializer序列化程序、JsonReader阅读器)
{
IDictionary expandoObject=null;
对象referenceObject=null;
while(reader.Read())
{
开关(reader.TokenType)
{
案例JsonToken.PropertyName:
string propertyName=reader.Value.ToString();
如果(!reader.Read())
抛出新的InvalidOperationException(“读取ExpandoObject时意外结束”);
对象v=读取值(序列化程序、读取器);
如果(propertyName==“$ref”)
{
var id=(v==null?null:Convert.ToString(v,CultureInfo.InvariantCulture));
referenceObject=serializer.ReferenceResolver.ResolveReference(序列化程序,id);
}
else if(propertyName==“$id”)
{
var id=(v==null?null:Convert.ToString(v,CultureInfo.InvariantCulture));
serializer.referencesolver.AddReference(序列化程序,id,(expandoObject??(expandoObject=newexpandoobject());
}
其他的
{
(expandoObject???(expandoObject=newexpandoobject())[propertyName]=v;
}
打破
案例JsonToken。评论:
打破
案例JsonToken.EndObject:
if(referenceObject!=null&&expandooobject!=null)
抛出reader.CreateException(“ExpandooObject同时包含$ref和real数据”);
返回referenceObject??expandooobject;
}
}
抛出reader.CreateException(“读取ExpandooObject时意外结束”);
}
公共覆盖布尔CanConvert(类型objectType)
{
返回(objectType==typeof(expandooobject));
}
公共覆盖布尔可写
{
获取{return false;}
}
}
公共静态类JsonTokenUtils
{
//改编自https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs
公共静态布尔IsPrimitiveToken(此JsonToken令牌)
{
交换机(令牌)
{
案例JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
案例JsonToken.Boolean:
案例JsonToken。未定义:
案例JsonToken.Null:
案例JsonToken。日期:
案例JsonToken.Bytes:
返回true;
违约:
var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Serialize };
settings.Converters.Add(new ObjectReferenceExpandoObjectConverter());
dynamic d = JsonConvert.DeserializeObject<ExpandoObject>(json, settings);
private static void Reffing(this IDictionary<string, object> current, Action<object> exchange,IDictionary<string, object> refdic)
{
object value;
if(current.TryGetValue("$ref", out value))
{
if(!refdic.TryGetValue((string) value, out value))
throw new Exception("ref not found ");
if (exchange != null)
exchange(value);
return;
}
if (current.TryGetValue("$id", out value))
{
refdic[(string) value] = current;
}
foreach (var kvp in current.ToList())
{
if (kvp.Key.StartsWith("$"))
continue;
var expandoObject = kvp.Value as ExpandoObject;
if(expandoObject != null)
Reffing(expandoObject,o => current[kvp.Key]=o,refdic);
var list = kvp.Value as IList<object>;
if (list == null) continue;
for (var i = 0; i < list.Count; i++)
{
var lEO = list[i] as ExpandoObject;
if(lEO!=null)
Reffing(lEO,o => list[i]=o,refdic);
}
}
}
var test = JsonConvert.DeserializeObject<ExpandoObject>(...);
var dictionary = new Dictionary<string, object>();
Reffing(test,null,dictionary);