2012-08-01 14 views
51

Sto cercando di spostare del codice per utilizzare ASP.NET MVC Web API generato dati JSON invece di SOAP Xml.NewtonSoft.Json Serializza e deserializza la classe con la proprietà di tipo IEnumerable <ISomeInterface>

ho incontrato un problema con la serializzazione e deserializzazione le proprietà di tipo:

IEnumerable<ISomeInterface>. 

Ecco un semplice esempio:

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; } 
} 
} 

posso serializzare le istanze di SampleGroup facilmente con:

var sz = JsonConvert.SerializeObject(sampleGroupInstance); 

Tuttavia, la deserializzazione corrispondente non riesce:

JsonConvert.DeserializeObject<SampleGroup>(sz); 

con questo messaggio di eccezione:

"Impossibile creare un'istanza di tipo JsonSerializationExample.ISample. Tipo è un interfaccia o classe astratta e non può essere instantated "

Se io traggo un JsonConverter posso decorare la mia proprietà come segue:.

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

Ecco il 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? 
    } 
} 

Questo convertitore risolve il problema della deserializzazione ma non riesco a capire come codificare il metodo WriteJson per far funzionare nuovamente la serializzazione

Qualcuno può dare una mano?

Si tratta di un modo "corretto" per risolvere il problema in primo luogo?

+0

In aggiunta alle risposte seguenti, è possibile sovrascrivere ['CanWrite'] (http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonConverter_CanWrite.htm) e restituire' false'. Vedi [Come utilizzare la serializzazione predefinita in un JsonConverter personalizzato] (https://stackoverflow.com/questions/29616596/how-to-use-default-serialization-in-a-custom-jsonconverter/29616648#29616648). – dbc

risposta

2

Nei miei progetti, questo pezzo di codice sempre lavorato come un serializzatore di default che serializza il valore specificato come se non ci fosse speciale convertitore:

serializer.Serialize(writer, value); 
+2

La serializzazione funziona correttamente. Il problema è la deserializzazione di una proprietà dichiarata come interfaccia. Implementare un CustomCreationConverter e passarlo a JsonConvert.DeserializeObject sembra essere la risposta. – AndyDBell

58

Non è necessario utilizzare JsonConverterAttribute, mantenere il vostro modello di pulita, utilizzare anche CustomCreationConverter, il codice è più semplice:

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

Poi:

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

Documentazione: Deserialize with CustomCreationConverter

+1

Grazie. Funziona bene – AndyDBell

+1

Grazie, mi hai salvato la giornata. Il mio JsonConverter non funzionava, non ho idea del perché. La tua soluzione funziona come un fascino ed è molto più semplice –

1

ho avuto questo lavoro:

conversione esplicita

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; 
    } 
4

grande soluzione, grazie! Ho preso la domanda del AndyDBell e la risposta di Cuong Le costruire un esempio con l'attuazione di due dell'interfaccia spettacolo diverso:

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(); 
    } 
} 

E una versione generica alla SampleConverter:

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

E 'abbastanza semplice e fuori dal il supporto fornito da scatola json.net, non resta che utilizzare i seguenti JsonSettings durante la serializzazione e deserializzazione:

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

e per De serialzing utilizzare il codice qui sotto:

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

Basta prendere nota delle JsonSerializerSettings di inizializzazione degli oggetti, che è importante per voi.

0

Avere che:

public interface ITerm 
{ 
    string Name { get; } 
} 

public class Value : ITerm... 

public class Variable : ITerm... 

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

sono riuscito conversione trucco attuazione che:

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; 
    } 
} 

Mi permette di serializzare e deserializzare in JSON come:

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()); 
7

ho risolto il problema utilizzando un'impostazione speciale per JsonSerializerSettings che si chiama TypeNameHandling.All

impostazione TypeNameHandling include digitare le informazioni durante la serializzazione JSON e leggere le informazioni di tipo in modo che i tipi di creare vengono creati quando la deserializzazione JSON

serializzazione:

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

Deserializzazione:

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

La classe YourClass potrebbe avere qualsiasi tipo di campi di tipo di base e sarà serializzato correttamente.

Problemi correlati