Avatar billede Melsvik Nybegynder
16. november 2011 - 11:23 Der er 5 kommentarer og
1 løsning

Json deserialize af abstract klasse

Jeg anvender Json til at serialisere nogle objecter, af hensyn til memory forbrug.

Json : http://json.codeplex.com/

Mit problem er at jeg kan ikke få Json til at serialisere mine abstracte objekter ordentligt.

Jeg bruger følgende kode til at gemme og loade mine objekter:

public void SaveToDisk(string sFile)
{
    using (var file = File.OpenWrite(sFile))
    {
        var serializer = new JsonSerializer();
        //TextWriter tw = new StreamWriter(file);
        var writer = new BsonWriter(file);
        serializer.Serialize(writer , this);
    }
}

public static PdfGraph LoadFromDisk(string sFile)
{
    PdfGraph graph;
    using (var file = File.OpenRead(sFile))
    {
        var serializer = new JsonSerializer();
        var reader = new BsonReader(file);
        graph = serializer.Deserialize<PdfGraph>(reader);
    }
    return graph;
}


Dette virker okay for alt andet end et abstract objekt som ligger et par lag nede i PdfGraph.

public abstract class numberFormatter
public class defaultNumberFormatter : numberFormatter
public class dateTimeNumberFormatter : numberFormatter

Problemet er denne linje (taget fra et subobjekt i PdfGraph):

public numberFormatter xNumberFormat = new defaultNumberFormatter();

I den instans af PdfGraph jeg gemmer er xNumberFormat af typen dateTimeNumberFormatter, men når jeg deserialisere mit gemte objekt får jeg et PdfGraph objekt hvor xNumberFormat er af typen defaultNumberFormatter.


Jeg har læst på Jsons hjemmeside at man skal anvende TypeNameHandling til denne slags problem.

Således:

public void SaveToDisk(string sFile)
{
    using (var file = File.OpenWrite(sFile))
    {
        var serializer = new JsonSerializer{TypeNameHandling = TypeNameHandling.All};

        //TextWriter tw = new StreamWriter(file);
        var writer = new BsonWriter(file);
        serializer.Serialize(writer, this);
    }
}

public static PdfGraph LoadFromDisk(string sFile)
{
    PdfGraph graph;
    using (var file = File.OpenRead(sFile))
    {
        var serializer = new JsonSerializer {TypeNameHandling = TypeNameHandling.All};
        var reader = new BsonReader(file);
        graph = serializer.Deserialize<PdfGraph>(reader);
    }
    return graph;
}

Typen a xNumberFormat bliver så også godt nok skrevet ned i mine serialiserede objekter:

"xNumberFormat":"$type":"Code.Ctrls.GraphPainter+dateTimeNumberFormatter, CodeCtrls.Net","sAxisTitle":null}

Men når jeg prøver at deserialisere dette objekt får jeg følgende fejl:

Error = {"Error setting value to 'sAxisTitle' on 'Code.Ctrls.GraphPainter+defaultNumberFormatter'."}

InnerException = {"Unable to cast object of type 'defaultNumberFormatter' to type 'dateTimeNumberFormatter'."}


Hvad skal jeg gøre for at få fremtryllet et objekt af typen dateTimeNumberFormatter?
Kan jeg hjælpe den mere på vej somehow or?

Undskyld den lange beskrivelse... Håber jeg fik gjort mig bare en smule forståelig.

Melsvik
Avatar billede Syska Mester
16. november 2011 - 11:32 #1
Dit eksemple er meget abstract og har har ikke lige helt tiden til at forstå det hele men ...

har du kigget på dette: http://stackoverflow.com/questions/8030538/how-to-implement-custom-jsonconverter-in-json-net-to-deserialize-a-list-of-base

Lyder til at være sammen problem med Abstract class.

mvh
Avatar billede Melsvik Nybegynder
16. november 2011 - 13:50 #2
Er lykkes mig at få min kode til at virke ved hjælp af Buzzzzs url.
Skal vist have trænet mine egne google skills :)
Så hvis du gider sende et svar returnere jeg med point.

Er dog en ret grim løsning jeg er kommet frem til.
Så hvis andre har en mere generel løsning vil jeg gerne smide en del extra point afsted.

Min løsning nu:

public void SaveToDisk(string sFile)
{
    var json = JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
    File.WriteAllText(sFile, json);
}

public static PdfGraph LoadFromDisk(string sFile)
{
    string json = File.ReadAllText(sFile);
    var graph = JsonConvert.DeserializeObject<PdfGraph>(json, new GraphConverter());
    return graph;
}
         

public class GraphConverter : JsonCreationConverter<PdfGraph>
{
    protected override PdfGraph Create(Type objectType, JObject jObject)
    {
        var output = new PdfGraph();

        var reader = jObject.CreateReader();
        while(reader.Read())
        {
            var value = reader.Value;
            if (value != null && value.ToString().Contains("dateTimeNumberFormatter"))
            {
                output.m_Painter = new GraphPainter
                {
                    xNumberFormat = new GraphPainter.dateTimeNumberFormatter()
                };
                break;
            }

            if (value != null && value.ToString().Contains("log10NumberFormat"))
            {
                output.m_Painter = new GraphPainter
                {
                    xNumberFormat = new GraphPainter.log10NumberFormat()
                };
                break;
            }

            if (value != null && value.ToString().Contains("customeNumberFormat"))
            {
                output.m_Painter = new GraphPainter
                {
                    xNumberFormat = new GraphPainter.customeNumberFormat()
                };
                break;
            }
        }

        return output;
    }
}

public abstract class JsonCreationConverter<T> : JsonConverter

    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }

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


Dette er kun brugbart fordi jeg ved at mit PdfGraph objekt er relativt simpelt. Men vokser mit PdfGraph object en dag med nye abstrakte typer får jeg problemer igen.
Så vil sætte pris på et par guldkorn til en bedre og mere generel løsning.

Melsvik
Avatar billede Syska Mester
16. november 2011 - 14:06 #3
Svar.

Men bare lad det stå åbenbart et par dage og ser om der sker mere.

Tror de fleste frameworks har problemet netop med abstract base classes, da den på en eller anden måde skal vise hvad den korrekte type er.
Avatar billede Melsvik Nybegynder
01. december 2011 - 09:05 #4
Vil gerne have lukket dette spørgsmål,
Så Buzzzz kan du ikke komme med et svar som jeg kan sende points efter, med mindre jeg tager meget fejl var dit sidste svar kun en kommentar ik?
Avatar billede Syska Mester
01. december 2011 - 10:52 #5
svar
Avatar billede Melsvik Nybegynder
01. december 2011 - 10:56 #6
Sådan, tak for hjælpen :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester