我将某些属性反序列化为Dictionary<string, object>
。
当我反序列化某些json时,它会使用Dictionary
个对象填充Int64
而不是Int32
。我希望它选择Int32
作为默认值,知道我可以使用转换时溢出的javascript数字。在这种情况下抛出异常是完全可以接受的。
有没有办法实现这一目标?我希望有一些很好的属性或方便的界面可以实现并添加到JsonSerializer
。而且我担心我必须深入到Json.NET的深处。
基本上我想有一些方法来控制对象的已知类型,这样我就可以得到Int32
而不是Int64
和DateTimes
而不是Strings
}。
答案 0 :(得分:21)
据我所知,没有内置的方法可以做到这一点。
此主题有issue,但已关闭。 作者对该问题的一些评论:
默认情况下,Json.NET将整数值作为Int64读取,因为无法知道该值是Int32还是Int64,而Int64不太可能溢出。对于类型化属性,反序列化器知道将Int64转换为Int32,但由于您的属性是无类型的,因此您将获得Int64。 [...]这就是Json.NET必须工作的方式。
最简单的解决方案是将类型更改为Dictionary<string, int>
,但我认为您不仅仅是阅读数字,因此仍然坚持使用object
。
另一种选择是使用Serialization Callbacks并手动将Int64
转换为Int32
或创建您自己的 Contract Resolver {{3并直接控制(反)序列化。
编辑:我创建了一个更具体的例子。
这是一个非常基本的转换器,仅适用于您的特定字典:
public class Int32Converter : JsonConverter {
public override bool CanConvert(Type objectType) {
// may want to be less concrete here
return objectType == typeof(Dictionary<string, object>);
}
public override bool CanWrite {
// we only want to read (de-serialize)
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
// again, very concrete
Dictionary<string, object> result = new Dictionary<string, object>();
reader.Read();
while (reader.TokenType == JsonToken.PropertyName) {
string propertyName = reader.Value as string;
reader.Read();
object value;
if (reader.TokenType == JsonToken.Integer)
value = Convert.ToInt32(reader.Value); // convert to Int32 instead of Int64
else
value = serializer.Deserialize(reader); // let the serializer handle all other cases
result.Add(propertyName, value);
reader.Read();
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
// since CanWrite returns false, we don't need to implement this
throw new NotImplementedException();
}
}
您可以使用属性来使用转换器来装饰成员,也可以使用JsonConverter来使用(反)序列化方法。这是我使用属性的示例:
[JsonObject]
public class MyObject {
[JsonConverter(typeof(Int32Converter))]
public Dictionary<string, object> Properties { get; set; }
}
这是我用来测试实现的代码:
class Program {
static void Main(string[] args) {
MyObject test = new MyObject();
test.Properties = new Dictionary<string, object>() { { "int", 15 }, { "string", "hi" }, { "number", 7 } };
Print("Original:", test);
string json = JsonConvert.SerializeObject(test);
Console.WriteLine("JSON:\n{0}\n", json);
MyObject parsed = JsonConvert.DeserializeObject<MyObject>(json);
Print("Deserialized:", parsed);
}
private static void Print(string heading, MyObject obj) {
Console.WriteLine(heading);
foreach (var kvp in obj.Properties)
Console.WriteLine("{0} = {1} of {2}", kvp.Key, kvp.Value, kvp.Value.GetType().Name);
Console.WriteLine();
}
}
没有转换器,结果将是:
Deserialized:
int = 15 of Int64
string = hi of String
number = 7 of Int64
并且转换器是:
Deserialized:
int = 15 of Int32
string = hi of String
number = 7 of Int32
答案 1 :(得分:2)
尝试
var variable = Convert.ToInt32(object)
迭代Dictionary<string,object>
一次并使用此object
重写Int32
,或每次阅读Int32
时进行object
转换。
答案 2 :(得分:2)
这对我很有用:
public class ParseNumbersAsInt32Converter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(long) || objectType == typeof(long?) || objectType == typeof(object);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value != null && reader.Value is long)
{
return Convert.ToInt32(reader.Value);
}
return reader.Value;
}
}
答案 3 :(得分:1)
我接受了Enzi的回答,因为这是我要求的。
然而,从那以后我改变了我的策略。
现在我正在反序列化到ChangeSet<T>
,而不是字典有一个带有更改的强类型Entity
(T)对象。它还有一个List<string>
,其中包含传入json中存在的属性的属性名称。然后,我使用自定义MediaFormatter在反序列化期间填充该列表。这样我得到一个强类型对象并正确反序列化所有属性,我从列表中知道当我想要进行批量操作时,我应该在我的T集合上设置哪些属性。
这样我基本上将我的实体用作DTO,而不必为不同的批处理操作提供无数不同的DTO。如果我自己这么说的话,那真是太棒了。 :)