DataContractJsonSerializer设置值扩展点

时间:2010-04-08 11:16:00

标签: c# .net json serialization

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

namespace ConsoleApplication1 {
  internal class Program {
    private static void Main(string[] args) {
      var pony = new Pony();
      var serializer = new DataContractJsonSerializer(pony.GetType());
      var example = @"{""Foo"":null}";
      var stream = new MemoryStream(Encoding.UTF8.GetBytes(example.ToCharArray()));
      stream.Position = 0;
      pony = (Pony) serializer.ReadObject(stream);
      // The previous line throws an exception.
    }
  }

  [DataContract]
  public class Pony {
    [DataMember]
    private int Foo { get; set; }
  }
}

有时,序列化会将Int32s上的转换错误设置为null。 有没有办法挂钩Json-serializer?

4 个答案:

答案 0 :(得分:2)

恕我直言,最好的办法是将Foo类型从Int32更改为System.Nullable<Int32>,因为这样可以最好地反映其语义(如果它可以为null)但是如果你不能修改它class AND如果使用DataContractJsonSerializer不是您的义务,Json.NET具有允许您执行此操作的扩展点(它也恰好是better performing)。

例如,您可以编写自定义类型转换器:

internal class NullableIntConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(int);
    }

    public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
    {
        if (reader.Value == null)
        {
            return default(int);
        }
        return int.Parse(reader.Value.ToString());
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new System.NotImplementedException();
    }
}

可以像这样注册和使用:

internal class Program
{
    private static void Main(string[] args)
    {
        var serializer = new JsonSerializer();
        serializer.Converters.Add(new NullableIntConverter());
        using (var reader = new StringReader(@"{""Foo"":null}"))
        using (var jsonReader = new JsonTextReader(reader))
        {
            var pony = serializer.Deserialize<Pony>(jsonReader);
            Console.WriteLine(pony.Foo);
        }
    }
}

答案 1 :(得分:1)

使其工作的最简单方法是将int设置为可为空的int。我通过示例代码进行了调试,看起来效果很好。

[DataContract]
public class Pony
{
    [DataMember]
    private int? Foo { get; set; }
}

答案 2 :(得分:1)

如果您使用的是.NET 3.5或更高版本(我希望您这样做),则可以使用JavaScriptSerializer代替。它更“动态”(不需要[DataContract]和[DataMember]属性),我之前从未遇到任何问题。 你只需拿起你想要的任何对象,任何类型并用它序列化:)

在.net 3.5中,它是System.Web.Extensions.dll的一部分,但在.NET 4中,此程序集现在是框架的一部分。

您可以轻松地将自己的自定义串行器代码添加到其中,并手动处理。通过这种方式,您可以获得所需的控件,并且您将知道哪些属性行为不当!

e.g:

void Main()
{
    var js = new JavaScriptSerializer();
    js.RegisterConverters(new[] { new PonySerializer() });
    var pony = js.Deserialize<Pony>(@"{""Foo"":""null""}");
    pony.Dump();
}

public class PonySerializer : JavaScriptConverter
{
    public override IEnumerable<Type> SupportedTypes
    {
        get { return new [] { typeof(Pony) }; }
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        if (type == typeof(Pony))
        {
            var pony = new Pony();
            int intValue;

            if (!int.TryParse(dictionary["Foo"] as string, out intValue))
                intValue = -1; // default value. You can throw an exception or log it or do whatever you want here

            pony.Foo = intValue;
            return pony;
        }
        return null;
    }
}

public class Pony
{
    public int Foo { get; set; }
}

P.S。这个例子是用LinqPad编写的,因此缺少使用,Main()等参数:P

答案 3 :(得分:0)

DataContractJsonSerializer具有您可以分配的DataContractSurrogate属性。您的代理类(IDataContractSurrogate的实现)可以在其GetDeserializedObject中自定义处理空值。