2016-05-19 25 views
1

Hinweis: Die Frage ist auf C# UWP beschränkt.Wie erreichen Sie die ideale JSerialisierung/Deserialisierung in C# UWP?

Der Traum:

public static class Serializer { 
    // return json string 
    public string Serialize(object obj) { ??? } 
    // return T from json string 
    public T Deserialize<T>(string jsonString) { ??? } 
} 

Closest Ich bin gekommen:

public static class Serializer 
{ 
    public static string Serialize(object obj, DataContractJsonSerializerSettings settings=null) 
    { 
     if (obj == null) { 
      throw new NullReferenceException(); 
     } 

     settings = settings ?? new DataContractJsonSerializerSettings(); 
     DataContractJsonSerializer jsonizer = new DataContractJsonSerializer(obj.GetType(), settings); 
     string jsonString = null; 
     using (MemoryStream stream = new MemoryStream()) 
     { 
      jsonizer.WriteObject(stream, obj); 
      stream.Position = 0; 
      StreamReader sr = new StreamReader(stream); 
      jsonString = sr.ReadToEnd(); 
     } 
     return jsonString; 
    } 

    public static T Deserialize<T>(string jsonString) 
    { 
     DataContractJsonSerializer jsonizer = new DataContractJsonSerializer(typeof(T)); 
     T obj; 
     using (Stream stream = GenerateStreamFromString(jsonString)) 
     { 
      obj = (T)jsonizer.ReadObject(stream); 
     } 
     return obj; 
    } 

    private static Stream GenerateStreamFromString(string s) 
    { 
     MemoryStream stream = new MemoryStream(); 
     StreamWriter writer = new StreamWriter(stream); 
     writer.Write(s); 
     writer.Flush(); 
     stream.Position = 0; 
     return stream; 
    } 
} 

Das Problem

Die Teillösung habe ich geschrieben Werke in einfachen Fällen. Es schlägt jedoch fehl, wenn der Untertyp des Objekts, das deserialisiert wird, schwierig (oder unmöglich) aus der JSON-Zeichenfolge zu bestimmen ist. Zum Beispiel

IList<Animal> animals = new List<Animal>(); 
animals.add(new Dog("Woofy")); 
animals.add(new Cat("Fluffy")); 

string json = Serializer.Serialize(animals); 
IList<Animal> result = Serializer.Deserialize<List<Animal>>(json); 
//^fails because subtype information was lost during serialization 

bool succeeded = result.get(0).Name.Equals("Woofy") && result.get(1).Name.Equals("Fluffy"); 

Was ich suche:

Eine Implementierung des Skeletts in "The Dream" angegeben, die die Fahrer in "The Problem" angegeben gibt. Kommentare willkommen.

+1

Wenn Sie 'DataContractJsonSerializer' verwenden, zu serialisiert und polymorphe Typen deserialisieren, müssen Sie es von allen möglichen Subtypen informieren, dass könnte angetroffen werden. Siehe [Bekannte Daten für Datenverträge] (https: // msdn.microsoft.com/de-de/library/ms730167.aspx), [DataContractSerializer und bekannte Typen] (https://stackoverflow.com/questions/9422662) und/oder [parse.com: SerializationException deserialisieren von JSON-Objekten mit "__type" Eigenschaft] (https://stackoverflow.com/questions/33746518). – dbc

+2

Oder wechseln Sie zu [tag: json.net] und aktivieren Sie ['TypeNameHandling'] (http://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm). Siehe z.B. [JSON-Serialisierung von Arrays mit polymorphen Objekten] (https://stackoverflow.com/questions/5186973). – dbc

Antwort

0

Die Antwort, die ich bei Ankunft:

using System; 
using System.Collections.Generic; 
using Newtonsoft.Json; 

namespace SerializationDemo 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Dog dog = new Dog() 
      { 
       Name = "Woofy", 
       HadWalkToday = true 
      }; 
      Cat cat = new Cat() 
      { 
       Name = "Fluffy", 
       ColorOfWhiskers = "Brown" 
      }; 
      IList<Animal> animals = new List<Animal>() 
      { 
       dog, 
       cat 
      }; 

      string json = Serializer.Serialize(animals); 
      IList<Animal> result = Serializer.Deserialize<List<Animal>>(json); 

      bool serializerSuccessful = dog.Equals(result[0]) && cat.Equals(result[1]); 
     } 
    } 

    public class Animal 
    { 
     public string Name { get; set; } 

     public override bool Equals(object obj) 
     { 
      var animal = obj as Animal; 
      return this.Name == animal.Name; 
     } 
    } 

    public class Dog : Animal 
    { 
     public bool HadWalkToday { get; set; } 

     public override bool Equals(object obj) 
     { 
      var dog = obj as Dog; 
      return this.HadWalkToday == dog.HadWalkToday && base.Equals(obj); 
     } 
    } 

    public class Cat : Animal 
    { 
     public string ColorOfWhiskers { get; set; } 

     public override bool Equals(object obj) 
     { 
      var cat = obj as Cat; 
      return this.ColorOfWhiskers == cat.ColorOfWhiskers && base.Equals(obj); 
     } 
    } 

    public static class Serializer 
    { 
     private static readonly JsonSerializerSettings settings = new JsonSerializerSettings() 
     { 
      TypeNameHandling = TypeNameHandling.All, 
      Formatting = Formatting.Indented 
     }; 

     public static string Serialize(object obj) 
     { 
      if (obj == null) 
      { 
       throw new NullReferenceException(); 
      } 

      string jsonString = JsonConvert.SerializeObject(obj, settings); 
      return jsonString; 
     } 

     public static T Deserialize<T>(string jsonString) 
     { 
      T obj = JsonConvert.DeserializeObject<T>(jsonString, settings); 
      return obj; 
     } 
    } 
} 
1

Ihre Serializer arbeitet völlig in Ordnung, wenn Sie die KnownType -attributes zu Ihrer Basisklasse hinzu:

[DataContract] 
[KnownType(typeof(Dog))] // add these 
[KnownType(typeof(Cat))] // lines 
public class Animal 
{ 
    [DataMember] 
    public string Name { get; set; } 
} 

[DataContract] 
public class Dog : Animal 
{ 
} 

[DataContract] 
public class Cat : Animal 
{ 
} 

Es ist notwendig, dass die DataContractJsonSerializer die Typinformation der Instanzen serialisiert zu bewahren. Sie können dies in der resultierenden serialisierten JSON sehen:

[{\"__type\":\"Dog:#My.Serialization.Sample.Project\",\"Name\":\"Woofy\"},{\"__type\":\"Cat:#My.Serialization.Sample.Project\",\"Name\":\"Fluffy\"}] 

Dort wird die zusätzliche Schlüssel __type ist, die die konkreten Informationen halten, dass das erste Objekt eine Dog Form Namespace My.Serialization.Sample.Project.


Aber wie @dbc bereits erwähnt, könnte man etwas besser dran JSON.NET verwendet, die leicht können Sie Ihre Liste serialisiert werden, ohne die Notwendigkeit, die Ihr Datenübertragungsobjekt mit all diesen Attributen zu schmücken. Selbst DataContract und DataMember werden nicht benötigt.

public class Animal 
{ 
    public string Name { get; set; } 
} 

public class Dog : Animal { } 

public class Cat : Animal { } 

es auf diese Weise

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }; 
string jsonNet = JsonConvert.SerializeObject(animals, settings); 
var jsonNetResult = JsonConvert.DeserializeObject<IList<Animal>>(jsonNet); 

Erträge verwenden, die zur Folge haben:

[{\"$type\":\"My.Serialization.Sample.Project.Dog, My.Assembly\",\"Name\":\"Woofy\"},{\"$type\":\"My.Serialization.Sample.Project.Cat, My.Assembly\",\"Name\":\"Fluffy\"}] 
0

Mit JsonSubTypes Sie haben mindestens zwei Möglichkeiten :

[JsonConverter(typeof(JsonSubtypes))] 
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Dog), "HadWalkToday")] 
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Cat), "ColorOfWhiskers")] 
public class Animal 
{ 
    public string Name { get; set; } 
} 

public class Dog : Animal 
{ 
    public bool HadWalkToday { get; set; } 
} 

public class Cat : Animal 
{ 
    public string ColorOfWhiskers { get; set; } 
} 

oder

[JsonConverter(typeof(JsonSubtypes), "Sound")] 
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")] 
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")] 
public class Animal 
{ 
    public virtual string Sound { get; } 
    public string Color { get; set; } 
} 

public class Dog : Animal 
{ 
    public override string Sound { get; } = "Bark"; 
    public string Breed { get; set; } 
} 

public class Cat : Animal 
{ 
    public override string Sound { get; } = "Meow"; 
    public bool Declawed { get; set; } 
} 

, die mit arbeitet:

[Test] 
public void Proof() 
{ 
    Dog dog = new Dog() 
    { 
     Name = "Woofy", 
     HadWalkToday = true 
    }; 
    Cat cat = new Cat() 
    { 
     Name = "Fluffy", 
     ColorOfWhiskers = "Brown" 
    }; 
    IList<Animal> animals = new List<Animal>() 
    { 
     dog, 
     cat 
    }; 

    string json = JsonConvert.SerializeObject(animals); 
    IList<Animal> result = JsonConvert.DeserializeObject<List<Animal>>(json); 

    Assert.IsTrue(result[0].GetType() == typeof(Dog)); 
    Assert.IsTrue(result[1].GetType() == typeof(Cat)); 
} 
Verwandte Themen