2010-04-08 2 views
3
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-сериализатору?DataContractJsonSerializer set value extension point

+1

У вас есть пример типа и строка json, которую мы можем использовать для воспроизведения, чтобы дать наиболее подходящий ответ? –

+0

Да, я добавил полный пример. – svinto

ответ

2

ИМХО лучше всего было бы изменить Foo типа от Int32 до System.Nullable<Int32>, как это будет лучше отражать семантику (если он может быть пустым), но если вы не можете изменить этот класс и при использовании 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); 
     } 
    } 
} 
+0

Он не может быть нулевым или, скорее, не должен быть нулевым, но иногда он имеет значение null для некоторых клиентов, но я не смог воспроизвести его. Переключение на JsonConverter может быть вариантом, хотя оно кажется немного опасным, поскольку могут быть тонкие различия, из-за которых все становится плохо. Я надеялся на подобное решение, используя DataContractJsonSerializer. Разве JsonConverter не был устаревшим на какое-то время? – svinto

+0

В последней версии Json.NET, которую я использовал для тестирования, она не устарела. –

+0

Я проверил это, он был отмечен как устаревший в .net 3.5, но не был помечен в .net 3.5 sp1. Я помню, что эта устаревшая маркировка является причиной того, что она не использует ее в первую очередь. – svinto

1

Самый простой способ заставить его работать было бы установить Int вместо этого следует использовать значение null. Я отлаживал код примера и, похоже, работает нормально.

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

Нельзя вставить нулевое значение, поэтому int? это не вариант.Я хочу иметь возможность обрабатывать их в каждом конкретном случае или, по крайней мере, получать информацию о том, какое свойство вызывает ошибку. – svinto

+1

Ум, если «это не должно быть возможно», то исключение, которое вы испытываете, является правильной вещью. С другой стороны, если вы хотите обрабатывать его как приемлемый ввод, используйте 'int?' и проверить позже. Вам может потребоваться отдельный «как-десериализованный» класс, который вы преобразовываете в ваш настоящий класс, чтобы быть абсолютно правильным. – mheyman

0

DataContractJsonSerializer имеет DataContractSurrogate свойство, которое можно назначить. Ваш суррогатный класс, который является реализацией IDataContractSurrogate, может в своем GetDeserializedObject выполнить вашу обработку нулевых значений.

+2

У вас есть пример, как это сделать? Кажется, я не понимаю. (Я могу просто увидеть GetDataContractType в суррогате, который вызывается один раз с typeof (Pony) как тип, который был передан, dunno, что с этим делать. – svinto

1

Если вы используете .NET 3.5 или новее (и я надеюсь, что вы это сделаете), вместо этого вы можете использовать JavaScriptSerializer. Это более «динамический» (не требует атрибутов [DataContract] и [DataMember]), и у меня никогда не было никаких проблем с ним раньше. Вы просто подобрать любой объект, который хотите, любой тип и сериализовать с ним :)

В .net 3.5 он был частью System.Web.Extensions.dll, но в .NET 4 эта сборка теперь является частью рамки.

Вы можете легко добавить к нему свой собственный код Serializer и обрабатывать его вручную. Таким образом, вы получите контроль, который вам нужен, и вы узнаете, какая из этих свойств неверно!

например:

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