我正在尝试实现自定义引用循环处理。我只需要编写空对象来代替嵌套对象。
预期结果
{ Id:1, Field:"Value", NestedObject:{Id:1}}
我创建了JsonConverter
public class SerializationConverter : JsonConverter
{
public override bool CanRead { get { return false; } }
public override bool CanWrite { get { return true; } }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Form) || typeof(Form).IsAssignableFrom(objectType);
}
private HashSet<Form> serializedForms = new HashSet<Form>();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
writer.WriteNull();
var f = (Form)value;
if (!serializedForms.Add(f))
writer.WriteRawValue("{Id:" + f.Id.Value + "}");
else
serializer.Serialize(writer, value);
}
}
但是,正如预期的那样,serializer.Serialize(writer, value)
内部调用的串行器会再次调用我的转换器。
我只是在对象已经序列化时才尝试替换序列化结果,否则使用默认的序列化行为。
答案 0 :(得分:2)
首先,我想提一下Json.Net有一个内置的PreserveReferencesHandling
设置,可以自动处理这类事情,而无需特殊的转换器。将PreserveReferencesHandling
设置为All
后,Json.Net会为每个对象分配内部引用ID,并将特殊$id
和$ref
属性写入JSON以跟踪引用。对于您的示例,JSON输出将如下所示:
{"$id":"1","Id":1,"Field":"Value","NestedObject":{"$ref":"1"}}
您会注意到这与您问题的所需输出非常相似。这样做的另一个好处是,可以很容易地将其反序列化回原始对象图中,同时保留所有引用,同样无需实现任何特殊内容。
但是我们暂时假设您有自己的理由想要实现自定义引用循环处理,并查看代码无效的原因。
当Json.Net遇到对象的JsonConverter
时,它假定转换器将处理写入该对象所需的任何JSON。因此,如果您想要包含某些属性,则必须自己编写。您可以使用序列化程序来帮助编写对象的部件,但是您不能只将整个对象传递给序列化程序并说“序列化这个”,因为它最终会重新调用你的转换器。
在大多数情况下,这样做会导致无限循环。在您的情况下,它没有,因为您在第一次调用WriteJson
时将表单添加到HashSet。当序列化程序第二次回调时,将采用另一个分支,因为表单已经在集合中。因此,对象的整个JSON最终为{Id:1}
,而不是您真正想要的。
防止序列化程序回调到转换器的一种方法是在转换器内创建JsonSerializer
的新实例,并使用该实例而不是传递给WriteJson
方法的实例。新实例将不会引用您的转换器,因此您的Form
将正常序列化。
不幸的是,这个想法也不起作用:如果你没有在内部序列化器上引用转换器,那么Json.Net就无法知道如何为{{{{{{{ 1}}!相反,它将被省略,因为我们将被迫将NestedObject
设置为ReferenceLoopHandling
以避免错误。所以你看,你有一个捕获22。
那么我们怎样才能让它发挥作用?好吧,让我们退后一步,重新定义你真正希望在输出方面发生的事情:
Ignore
。Id
,Id
和Field
。请注意,在这两种情况下,我们都希望输出NestedObject
,因此我们可以简化逻辑:
Id
和Field
。为了简单起见,我们可以使用NestedObject
来收集我们想要输出的属性,然后在最后将其写入JObject
。
以下是修订后的代码:
writer
现在让我们测试一下:
public class SerializationConverter : JsonConverter
{
public override bool CanRead { get { return false; } }
public override bool CanWrite { get { return true; } }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Form) || typeof(Form).IsAssignableFrom(objectType);
}
private HashSet<Form> serializedForms = new HashSet<Form>();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Form f = (Form)value;
JObject jo = new JObject();
jo.Add("Id", f.Id);
if (serializedForms.Add(f))
{
jo.Add("Field", f.Field);
if (f.NestedObject != null)
{
jo.Add("NestedObject", JToken.FromObject(f.NestedObject, serializer));
}
}
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
这是输出:
class Program
{
static void Main(string[] args)
{
Form form = new Form
{
Id = 1,
Field = "Value",
};
form.NestedObject = form;
JsonSerializerSettings settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new SerializationConverter() },
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
};
string json = JsonConvert.SerializeObject(form, settings);
Console.WriteLine(json);
}
}
class Form
{
public int Id { get; set; }
public string Field { get; set; }
public Form NestedObject { get; set; }
}
到目前为止看起来不错。更严格的事情怎么样:
{"Id":1,"Field":"Value","NestedObject":{"Id":1}}
输出:
class Program
{
static void Main(string[] args)
{
List<Form> forms = new List<Form>
{
new Form
{
Id = 1,
Field = "One",
NestedObject = new Form
{
Id = 2,
Field = "Two"
}
},
new Form
{
Id = 3,
Field = "Three"
},
new Form
{
Id = 4,
Field = "Four"
},
new Form
{
Id = 5,
Field = "Five"
}
};
forms[0].NestedObject.NestedObject = forms[3];
forms[1].NestedObject = forms[0].NestedObject;
forms[2].NestedObject = forms[1];
JsonSerializerSettings settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new SerializationConverter() },
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(forms, settings);
Console.WriteLine(json);
}
}
编辑
如果您的[
{
"Id": 1,
"Field": "One",
"NestedObject": {
"Id": 2,
"Field": "Two",
"NestedObject": {
"Id": 5,
"Field": "Five"
}
}
},
{
"Id": 3,
"Field": "Three",
"NestedObject": {
"Id": 2
}
},
{
"Id": 4,
"Field": "Four",
"NestedObject": {
"Id": 3
}
},
{
"Id": 5
}
]
类包含大量字段,则可能需要使用反射而不是在转换器中单独列出属性。以下是使用反射的Form
方法:
WriteJson