Laden...

JSON Dynamic Property Deserialize

Erstellt von Duesmannr vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.455 Views
D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren
JSON Dynamic Property Deserialize

Mahlzeit,

mit Stackoverflow/Google und die SuFu hier, komme ich nicht weiter.

Ich erhalte ein dynamisches Json Objekt von einer Api.


{
   "container":{
      "mirror_1":{
         "links":[
            "https:\/\/google.de",
            "https:\/\/mycsharp.de"
         ],
         "backup":[
            [
               "https:\/\/google.de",
               "https:\/\/mycsharp.de"
            ]
         ]
      },
      "mirror_2":{
         "links":[
            "https:\/\/google.de",
            "https:\/\/mycsharp.de"
         ],
         "backup":[
            [
             "https:\/\/google.de",
             "https:\/\/mycsharp.de"
            ]
         ]
      }
   }
}

Dynamisch an dem JSON sind die Mirrors. Basierend auf der Anzahl kann 1 bis n vorkommen.
Also


mirror_{n}

Ich weiß aber nicht wie ich das in Klassen deserialisieren soll.
In einem anderen Projekt konnte ich einfach ein Dictionary nehmen, funktioniert hier nicht.

Auch mit mit einem Custom DefaultContractResolver versucht wie hier beschrieben
Stackoverflow
läuft es auch nicht.

So sehen die Klassen generiert aus


public class Rootobject
    {
        public Container container { get; set; }
    }

    public class Container
    {
        public Mirror_1 mirror_1 { get; set; }
        public Mirror_2 mirror_2 { get; set; }
    }

    public class Mirror_1
    {
        public string[] links { get; set; }
        public string[][] backup { get; set; }
    }

    public class Mirror_2
    {
        public string[] links { get; set; }
        public string[][] backup { get; set; }
    }

Habt Ihr ggfs. Ansätze?

Grüße

16.806 Beiträge seit 2008
vor 3 Jahren

Da das Json hier in der Grundstruktur halt schon "suboptimal" besser gesagt beschissen ist bringt Dir das generierte Zeugs natürlich nichts.
Du wirst Dir wohl oder übel nen eigenen Visitor basteln müssen, der die Nodes anhand des Namens und der Nummer erkennt und dann parst.

Generisch ist hier nicht (ohne weiteres).

Da hat offenbar jemand ne API erstellt, der das nicht hätte tun sollen 😃

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

Die Response ist genau so gut, wie die "dokumentierte" Api..

Was meinst du denn explizit mit Visitor?

2.078 Beiträge seit 2012
vor 3 Jahren

Ich kenne nur Json.NET bzw. Newtonsoft.Json, aber da gibt's z.B. die JObject-Klasse.
Damit kannst Du dein Json dann parsen und ungefähr so flexibel durchsuchen, wie es mit LINQ to XML möglich ist - ist vermutlich auch daran orientiert.

Was Abt genau meint, weiß ich auch nicht, aber mit JObject wäre dein Vorhaben auf jeden Fall möglich.

P
441 Beiträge seit 2014
vor 3 Jahren

Du könntest das mit einem Dictionary machen.


class Root {
 public Dictionary<string, Mirror> Container { get; set; }
}

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

Auf JObject bin ich auch schon gekommen.

Habe jetzt eine Lösung, auch wenn ich die nicht wirklich schön finde.

Verzeiht mir die temporären Variablennamen, aber beim durchgehenden Debuggen, hatte ich keine Lust, jedesmal sinnvolle Namen zu nehmen.


JObject objec = JObject.Parse(responseString);

JObject klgfds = (JObject)objec["container"]["mirror_1"];

String jkgfsd = klgfds.ToString(Formatting.None);

Mirror1 mirro1 = JsonConvert.DeserializeObject<Mirror1>(jkgfsd);

Das funktioniert so. Mirror1 ist das equivalent zu Mirror_1 wie im Startpost.

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

Du könntest das mit einem Dictionary machen.

Nein, dass funktioniert leider nicht.
Der konvertiert das dann nicht. Und das Dictionary ist null.

16.806 Beiträge seit 2008
vor 3 Jahren

Was meinst du denn explizit mit Visitor?

Im Fall von Newtonsoft.Json ein JsonConverter sowie das dazuhörige Attribute.

Damit kannst Du dann alle Nodes ermitteln, die mit Mirror_ beginnen und entsprechend manuell serialisieren.

So hast Du als Objekt eine saubere Liste, auch wenn die Json Response murks ist.

Wie gesagt; mit Standard-Zeugs kommst nicht weit - einfach, weil Du auch kein standardisiertes Ausgangsmaterial hast.

Dein Code so hilft Dir nicht, ausser Du willst wirklich n-Mirrors von Hand programmieren; wenn Du das willst, dann kannste Dir gleich einfach ne Container Klasse mit n-Mirror Properties bauen. Gewonnen hast also nichts.
Aber ich hab verstanden, dass Du das gar nicht willst.

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

Dein Code so hilft Dir nicht, ausser Du willst wirklich n-Mirrors von Hand programmieren; wenn Du das willst, dann kannste Dir gleich einfach ne Container Klasse mit n-Mirror Properties bauen. Gewonnen hast also nichts.
Aber ich hab verstanden, dass Du das gar nicht willst.

Naja nicht ganz, derzeit sieht es so aus.


JObject responseObject = JObject.Parse(responseString);

            JObject containerObject = (JObject)responseObject["container"];

            List<Mirror> mirrors = new List<Mirror>();

            for (int i = 1; i < containerObject.Count + 1; i++)
            {
                JObject mirrorObject = (JObject)responseObject["container"][$"mirror_{i}"];

                Mirror mirror = JsonConvert.DeserializeObject<Mirror>(mirrorObject.ToString(Formatting.None));
                mirror.MirrorValue = i;

                mirrors.Add(mirror);
            }

Das ist aber auch keine Lösung und werde deinen Ansatz verfolgen, weil beim nächsten Endpunkt, dass gleiche Problem auftritt nur noch schlimmer.

Der Endpunkt holt mir die primären Informationen von allen Containern die existieren (keine Mirrors) und dabei steht das als Dokumentation:
Please note every Object child of the container-Node starts with a trailing _.

Im Endeffekt hätte ich dann pro Container eine eigene Klasse mit unterschiedlichen Namen.

16.806 Beiträge seit 2008
vor 3 Jahren

Mitm sauberen Serializer würde das so aussehen. Verstehe nicht, wieso Du nicht die Mittel von Json.NET nutzen magst...
Das Tooling von Json.NET deckt solche Fälle ja ab.


        public override ContainerRecord ReadJson(JsonReader reader, Type objectType, ContainerRecord existingValue,
            bool hasExistingValue,
            JsonSerializer serializer)
        {
            var containerObject = JObject.Load(reader);
            for (int i = 1; i < containerObject.Count + 1; i++)
            {
                var mirrorObject = containerObject[$"mirror_{i}"].ToObject<MirrorRecord>();
                existingValue.Mirrors.Add(mirrorObject);
            }

             return existingValue;
        }

Ist natürlich sehr anfällig wenn man mal was anderes als nen Mirror in der Objektkiste hat..

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

Mitm sauberen Serializer würde das so aussehen. Verstehe nicht, wieso Du nicht die Mittel von Json.NET nutzen magst...

Das ist aber auch keine Lösung und werde deinen Ansatz verfolgen[...]

Wie schon geschrieben, wollte ich ja auf die custom converter gehen.
Dein Code funktioniert fast. Du kannst beim override nicht die Parametertypen und den Rückgabetyp ändern. Noch sind wir nicht in C# 9.

Ist natürlich sehr anfällig wenn man mal was anderes als nen Mirror in der Objektkiste hat..

Das fange ich voher ab.

16.806 Beiträge seit 2008
vor 3 Jahren

Du kannst beim override nicht die Parametertypen und den Rückgabetyp ändern.

MyContainerRecordConverter : JsonConverter<ContainerRecord>

Nimm Dir doch 2-3 Minuten Zeit ein Blick in die Doku zu werfen, statt nun irgendwas mit C# 9 zu vermischen 😉

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 3 Jahren

MyContainerRecordConverter : JsonConverter<ContainerRecord>

Stimmt.. vergessen.

Nimm Dir doch 2-3 Minuten Zeit ein Blick in die Doku zu werfen, statt nun irgendwas mit C# 9 zu vermischen 😉

Wird auch noch gemacht, nachdem ich sehe, dass ich doch jetzt ziemlich oft die Custom Converter gebrauche.

Aber danke für die Hilfe.