【问题标题】:Serializing and deserializing TimeSpan values to a property of type Object将 TimeSpan 值序列化和反序列化为 Object 类型的属性
【发布时间】:2014-12-10 19:45:49
【问题描述】:

我有一个类,它有一个 Object 类型的字段,其中可能包含一个 TimeSpan 对象。

我正在序列化这个和反序列化这个:

public class MyObject
{
    public object myTimeSpan { get; set; }
}

...

var myObject = new MyObject { myTimeSpan = TimeSpan.FromDays(2) };
var json = JsonConvert.SerializeObject(myObject);

...

var duplicateObject = JsonConvert.DeserializeObject<MyObject>(json);

当我这样做时,duplicateObject.myTimeSpan 包含字符串“2:00:00”。

如何将其反序列化为 TimeSpan 对象?

两点:

  1. 我不处理已在其他地方序列化的字符串。我要反序列化的唯一字符串是我序列化的字符串。
  2. 该字段必须是对象类型 - 它可能包含一个字符串或一个 DateTime,或者它可能包含一个 TimeSpan。目前,这只是我遇到问题的 TimeSpan。

【问题讨论】:

  • 您的代码似乎不完整。您实际序列化的“selectionCriteria”是什么?
  • 我猜你的意思是var json = JsonConvert.SerializeObject(myObject);。如果我这样做,我会得到 JSON {"myTimeSpan":"2.00:00:00"},并且复制的对象有 2 天的时间跨度。
  • 对不起。剪切和粘贴错误 - 已修复。
  • 当底层字段是对象类型时,我没有得到 TimeSpan 对象。
  • 您可能需要实现一个自定义JsonConverter,它可以尝试将TimeSpan 的字符串表示解析回一个实际的TimeSpan 对象。

标签: c# json serialization json.net


【解决方案1】:

Json.Net 有一个TypeNameHandling 设置用于处理未知类型,以便它们可以正确反序列化。打开此设置后,它会导致 Json.Net 将特殊的 $type 属性插入 JSON,然后在反序列化时用作提示。不幸的是,此设置似乎不适用于“简单”类型,例如 TimeSpan,因为它们的值被序列化为字符串而不是对象。

要解决这个问题,我建议制作一个使用相同想法的自定义JsonConverter。转换器不是直接输出对象的字符串值,而是输出具有两个属性的子对象表示:typevalue。子对象的type 属性将包含对象的程序集限定类型名称,而value 属性将包含实际的序列化值。在反序列化时,转换器将查找 type 属性以了解要从 value 属性实例化的对象类型。这种方法的好处是您不必向模型类添加任何额外的属性或逻辑。

这是它在代码中的样子:

class UnknownObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject jo = new JObject();
        jo["type"] = value.GetType().AssemblyQualifiedName;
        jo["value"] = JToken.FromObject(value, serializer);
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        Type type = Type.GetType(jo["type"].ToString(), throwOnError: true);
        return jo["value"].ToObject(type, serializer);
    }
}

要使用转换器,只需使用 [JsonConverter] 属性装饰类中需要特殊处理的任何 object 属性,如下所示:

public class MyObject
{
    [JsonConverter(typeof(UnknownObjectConverter))]
    public object MyValue { get; set; }
}

这是一个往返演示,展示了这如何适用于几种不同的类型。

class Program
{
    static void Main(string[] args)
    {
        List<MyObject> list = new List<MyObject>
        {
            new MyObject { MyValue = TimeSpan.FromDays(2) },
            new MyObject { MyValue = "foo" },
            new MyObject { MyValue = new DateTime(2014, 12, 20, 17, 06, 44) },
            new MyObject { MyValue = new Tuple<int, bool>(23, true) }
        };

        string json = JsonConvert.SerializeObject(list, Formatting.Indented);
        Console.WriteLine(json);
        Console.WriteLine();

        list = JsonConvert.DeserializeObject<List<MyObject>>(json);
        foreach (MyObject obj in list)
        {
            Console.WriteLine(obj.MyValue.GetType().Name + ": " + obj.MyValue.ToString());
        }
    }
}

这是输出:

[
  {
    "MyValue": {
      "type": "System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
      "value": "2.00:00:00"
    }
  },
  {
    "MyValue": {
      "type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
      "value": "foo"
    }
  },
  {
    "MyValue": {
      "type": "System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
      "value": "2014-12-20T17:06:44"
    }
  },
  {
    "MyValue": {
      "type": "System.Tuple`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
      "value": {
        "Item1": 23,
        "Item2": true
      }
    }
  }
]

TimeSpan: 2.00:00:00
String: foo
DateTime: 12/20/2014 5:06:44 PM
Tuple`2: (23, True)

【讨论】:

  • 在ReadJson下,首先需要检查是否有空类型,否则在序列化空类型时会出现异常。 if (reader.TokenType == JsonToken.Null) 返回 null;
  • 很难找到 TimeSpan 的序列化结果示例。我注意到发送 int 4 会产生四天的时间跨度,但我无法像 0.25 那样传递 double。幸运的是,我在您的回答中注意到格式“d.hh:mm:ss”。谢谢和 +1。
猜你喜欢
  • 2020-02-19
  • 1970-01-01
  • 2012-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 2012-07-30
相关资源
最近更新 更多