我有一个转换器,我只想在反序列化时使用。所以我将CanWrite设置为false,这很好,所有序列化都很好。然后,Json字符串包含一个对象图,其中有一个带有SantaClause项目数组的SantaClauseCollection和一个$ type表示它们是具体类型的SantaClause。
然而,当它在反序列化时遇到SantaClaus的集合时,它从不调用CanConvert(我有一个断点并看到SantaClausCollection,点击F5继续,然后当遇到集合中的项目时应再次达到断点) SantaClaus,但它没有。当它到达SantaClaus项目时,它不会尝试调用CanConvert。甚至没有为该项调用CanConvert来检查我的转换器是否会处理它,而是尝试反序列化它本身,这不会起作用,因为该类没有默认构造函数,也没有具有属性名匹配约定的构造函数:
无法找到用于类型SantaClaus的构造函数。一类 应该有一个默认的构造函数,一个构造函数 参数或用JsonConstructor属性标记的构造函数。
我理解为什么会出现这个错误,但问题是它表明Json.net试图反序列化该对象,而不是调用CanConvert检查并查看我的转换器是否想要处理反序列化。
为什么没有为集合中的每个项目调用CanConvert?
我的转换器:
class SantaClaus2JsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SantaClaus);
}
/// <summary>
/// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally.
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<SantaClausEx>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;//We only need this converter when reading.
}
}
}
SantaClausEx只是从SantaClaus继承,添加一个带有重命名参数的构造函数来匹配属性:
class SantaClaus //a third party class I can't modify
{
string Name {get;set;}
public SantaClaus(string santaClauseName) { this.Name = santaClauseName }
}
class SantaClausEx:SantaClaus
{
//provide a constructor with param names matching property names
public SantaClausEx(string name) : base(name)
}
Json.net无法对SantaClaus进行反序列化,但它可以反序列化SantaClauseEx。
我在任何地方都使用了SantaClauseEx类,它运行得很好,但我想制作一个转换器来自动执行此操作。
这就是Json对于该系列的看法:
SantaClausCollection: [
{
$type: "SantaClaus, XMasClasses.NET20"
Name: "St. Bob"
},
{
$type: "SantaClaus, XMasClasses.NET20"
Name: "St. Jim"
}
]
答案 0 :(得分:1)
我有一个类似的问题反序列化从基类继承的对象(类似于你需要反序列化SantaClauseEx对象,但它们都被定义为SantaClause对象)。问题在于JSon.Net无法识别子类型。
答案 1 :(得分:1)
我想您已将转换器添加到设置对象中的Converters
集合中。
我用转换器编写简单的测试工作
public class SantaClausJsonTest
{
public SantaClausJsonTest()
{
Settings = new JsonSerializerSettings();
Settings.TypeNameHandling = TypeNameHandling.Objects;
Settings.Converters.Add(new SantaClaus2JsonConverter());
}
private JsonSerializerSettings Settings;
[Fact]
public void SerializeAndDeserialize()
{
var collection = new []
{
new SantaClaus("St. Bob"),
new SantaClaus("St. Jim"),
};
var serialized = JsonConvert.SerializeObject(collection, Settings);
Console.WriteLine(serialized);
Assert.False(string.IsNullOrEmpty(serialized));
var deserialized = JsonConvert.DeserializeObject<SantaClaus[]>(serialized, Settings);
Console.WriteLine(deserialized.GetType().ToString());
Assert.NotNull(deserialized);
Assert.True(deserialized.Any(a => a.Name == "St. Bob"));
Assert.True(deserialized.Any(a => a.Name == "St. Jim"));
}
}
public class SantaClaus
{
public SantaClaus(string santaClauseName)
{
Name = santaClauseName;
}
public string Name { get; private set; }
}
public class SantaClaus2JsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SantaClaus);
}
/// <summary>
/// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally.
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var name = string.Empty;
while (reader.Read())
{
if (reader.TokenType == JsonToken.String && reader.Path.EndsWith("Name"))
{
name = reader.Value as string;
}
if (reader.TokenType == JsonToken.EndObject)
{
break;
}
}
return Activator.CreateInstance(objectType, name);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;//We only need this converter when reading.
}
}
答案 2 :(得分:0)
一旦我得到Rudus的答案,我就用我最初的尝试确定了这个问题。当你有一个没有默认构造函数的类型时,他很棒,但可以将属性值映射到其他构造函数之一,对我的特定情况来说肯定更容易。
如果出于某种原因,你确实需要像我最初尝试做的那样,在反序列化时创建一个不同类型的东西,我能够让它工作。
public class SantaClaus2JsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SantaClaus);
}
/// <summary>
/// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally.
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//temporarily switch off name handling so it ignores "SantaClaus" type when
//explicitely deserialize as SantaClausEx
//This could cause issues with nested types however in a more complicated object graph
var temp = serializer.TypeNameHandling;
serializer.TypeNameHandling = TypeNameHandling.None;
var desr = serializer.Deserialize<SantaClausEx>(reader);
serializer.TypeNameHandling = temp;//restore previous setting
return desr;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotSupportedException();
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { false; } } //only for reading
}