it-swarm.com.de

NewtonSoft.Json Serialize- und Deserialize-Klasse mit der Eigenschaft vom Typ IEnumerable <ISomeInterface>

Ich versuche, etwas Code zu verschieben, um von ASP.NET MVC Web API generierte Json-Daten anstelle von SOAP XML zu verwenden.

Ich habe ein Problem mit der Serialisierung und Deserialisierung der Eigenschaften des Typs:

IEnumerable<ISomeInterface>.

Hier ist ein einfaches Beispiel:

public interface ISample{
  int SampleId { get; set; }
}
public class Sample : ISample{
  public int SampleId { get; set; }
}
public class SampleGroup{
  public int GroupId { get; set; }
  public IEnumerable<ISample> Samples { get; set; }
 }
}

Ich kann Instanzen von SampleGroup problemlos serialisieren mit:

var sz = JsonConvert.SerializeObject( sampleGroupInstance );

Die entsprechende Deserialisierung schlägt jedoch fehl:

JsonConvert.DeserializeObject<SampleGroup>( sz );

mit dieser Ausnahmemeldung:

"Konnte keine Instanz des Typs JsonSerializationExample.ISample erstellen. Type ist eine Schnittstelle oder abstrakte Klasse und kann nicht instanziiert werden."

Wenn ich einen JsonConverter ableite, kann ich mein Eigentum folgendermaßen dekorieren:

[JsonConverter( typeof (SamplesJsonConverter) )]
public IEnumerable<ISample> Samples { get; set; }

Hier ist der JsonConverter: 

public class SamplesJsonConverter : JsonConverter{
  public override bool CanConvert( Type objectType ){
    return ( objectType == typeof (IEnumerable<ISample>) );
  }

  public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ){
    var jA = JArray.Load( reader );
    return jA.Select( jl => serializer.Deserialize<Sample>( new JTokenReader( jl ) ) ).Cast<ISample>( ).ToList( );
  }

  public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ){
    ... What works here?
  }
}

Dieser Konverter löst das Deserialisierungsproblem, aber ich kann nicht verstehen, wie die WriteJson-Methode codiert wird, damit die Serialisierung wieder funktioniert.

Kann jemand helfen? 

Ist dies ein "richtiger" Weg, um das Problem überhaupt zu lösen?

60
AndyDBell

Sie brauchen nicht JsonConverterAttribute, halten Sie Ihr Modell sauber, verwenden Sie auch CustomCreationConverter, der Code ist einfacher:

public class SampleConverter : CustomCreationConverter<ISample>
{
    public override ISample Create(Type objectType)
    {
        return new Sample();
    }
}

Dann:

var sz = JsonConvert.SerializeObject( sampleGroupInstance );
JsonConvert.DeserializeObject<SampleGroup>( sz, new SampleConverter());

Dokumentation: Deserialize mit CustomCreationConverter

65
cuongle

Es ist recht einfach und die Unterstützung von json.net ist sofort verfügbar. Sie müssen nur die folgenden JsonSettings verwenden, um das Serialisieren und Deserialisieren durchzuführen:

JsonConvert.SerializeObject(graph,Formatting.None, new JsonSerializerSettings()
{
    TypeNameHandling =TypeNameHandling.Objects,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});

und für Deserialzing den folgenden Code verwenden: 

JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bData),type,
    new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Objects}
);

Notieren Sie sich nur den für Sie wichtigen Objektinitialisierer JsonSerializerSettings. 

17
Sunil S

Ich habe dieses Problem mit einer speziellen Einstellung für JsonSerializerSettings mit dem Namen TypeNameHandling.All gelöst.

Die Einstellung TypeNameHandling enthält Typinformationen bei der Serialisierung von JSON- und Lesetypinformationen, sodass die Erstellungstypen bei der Deserialisierung von JSON erstellt werden

Serialisierung:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var text = JsonConvert.SerializeObject(configuration, settings);

Deserialisierung:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var configuration = JsonConvert.DeserializeObject<YourClass>(json, settings);

Die Klasse YourClass kann alle Arten von Basistypfeldern haben und wird ordnungsgemäß serialisiert.

10
adam.bielasty

Danke für die großartige Lösung! Ich nahm die Frage von AndyDBell und die Antwort von Cuong Le, um ein Beispiel mit zwei verschiedenen Schnittstellenimplementierungen zu erstellen:

public interface ISample
{
    int SampleId { get; set; }
}

public class Sample1 : ISample
{
    public int SampleId { get; set; }
    public Sample1() { }
}


public class Sample2 : ISample
{
    public int SampleId { get; set; }
    public String SampleName { get; set; }
    public Sample2() { }
}

public class SampleGroup
{
    public int GroupId { get; set; }
    public IEnumerable<ISample> Samples { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        //Sample1 instance
        var sz = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1},{\"SampleId\":2}]}";
        var j = JsonConvert.DeserializeObject<SampleGroup>(sz, new SampleConverter<Sample1>());
        foreach (var item in j.Samples)
        {
            Console.WriteLine("id:{0}", item.SampleId);
        }
        //Sample2 instance
        var sz2 = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1, \"SampleName\":\"Test1\"},{\"SampleId\":2, \"SampleName\":\"Test2\"}]}";
        var j2 = JsonConvert.DeserializeObject<SampleGroup>(sz2, new SampleConverter<Sample2>());
        //Print to show that the unboxing to Sample2 preserved the SampleName's values
        foreach (var item in j2.Samples)
        {
            Console.WriteLine("id:{0} name:{1}", item.SampleId, (item as Sample2).SampleName);
        }
        Console.ReadKey();
    }
}

Und eine generische Version zum SampleConverter:

public class SampleConverter<T> : CustomCreationConverter<ISample> where T: new ()
{
    public override ISample Create(Type objectType)
    {
        return ((ISample)new T());
    }
}
4
Daniel Melo

In meinen Projekten arbeitete dieser Code immer als Standard-Serialisierer, der den angegebenen Wert so serialisiert, als wäre kein spezieller Konverter vorhanden:

serializer.Serialize(writer, value);
2
fero

Ich habe das zur Arbeit bekommen:

explizite Konvertierung

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
                                    JsonSerializer serializer)
    {
        var jsonObj = serializer.Deserialize<List<SomeObject>>(reader);
        var conversion = jsonObj.ConvertAll((x) => x as ISomeObject);

        return conversion;
    }
1
dvr

Das haben:

public interface ITerm
{
    string Name { get; }
}

public class Value : ITerm...

public class Variable : ITerm...

public class Query
{
   public IList<ITerm> Terms { get; }
...
}

Ich habe es geschafft, den Umsetzungstrick zu implementieren:

public class TermConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var field = value.GetType().Name;
        writer.WriteStartObject();
        writer.WritePropertyName(field);
        writer.WriteValue((value as ITerm)?.Name);
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var properties = jsonObject.Properties().ToList();
        var value = (string) properties[0].Value;
        return properties[0].Name.Equals("Value") ? (ITerm) new Value(value) : new Variable(value);
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof (ITerm) == objectType || typeof (Value) == objectType || typeof (Variable) == objectType;
    }
}

Es erlaubt mir, in JSON zu serialisieren und zu deserialisieren: 

string JsonQuery = "{\"Terms\":[{\"Value\":\"This is \"},{\"Variable\":\"X\"},{\"Value\":\"!\"}]}";
...
var query = new Query(new Value("This is "), new Variable("X"), new Value("!"));
var serializeObject = JsonConvert.SerializeObject(query, new TermConverter());
Assert.AreEqual(JsonQuery, serializeObject);
...
var queryDeserialized = JsonConvert.DeserializeObject<Query>(JsonQuery, new TermConverter());
0