Runtime: Unterstützt "dynamisches" und beschreibbares DOM

Erstellt am 29. Mai 2019  ·  47Kommentare  ·  Quelle: dotnet/runtime

Unterstützt JsonSerializer.Parse(String, Type, JsonSerializerOptions) den dynamischen Rückgabetyp ExpandoObject ?

Etwas wie das:
dynamic p = JsonSerializer.Parse(json, typeof(ExpandoObject));

area-System.Text.Json enhancement json-functionality-doc

Hilfreichster Kommentar

Ich habe auch einen Anwendungsfall dafür - ich möchte nur eine REST-API mit HttpClient aufrufen und eine einzelne Eigenschaft aus der Antwort abrufen. Ich möchte keine dedizierte Klasse erstellen, nur um die Antwort zu analysieren ... Mit JSON.NET könnte ich einfach "dynamisch" verwenden und auf die Eigenschaft der Wahl zugreifen.

Alle 47 Kommentare

Nein, diese Funktion wird derzeit nicht unterstützt, aber wir sollten dies für vNext in Betracht ziehen. Haben Sie eine Beispielverwendung, um die Funktionsanfrage zu motivieren?

Als Zukunft markieren.

System.NotSupportedException : The collection type 'System.Dynamic.ExpandoObject' is not supported.

@ahsonkhan GraphQL ist ein gutes Beispiel.

Die Spezifikation empfiehlt JSON, ist jedoch nicht an eine bestimmte Serialisierung in der Antwort gebunden.
Dies impliziert, dass das "Daten"-Feld der Antwort von der Art ist: dynamisch. Da man es nicht ableiten kann.
Ohne ExpandoObject macht die Deserialisierung dynamisch eine Art JSON-Erstellung. Der Zugriff auf diese abstrakten dynamischen "Daten" muss also in dem Wissen erfolgen, dass diese Dynamik tatsächlich ein JToken ist.

Mit ExpandoObject denke ich, dass wir den Zugriff auf die dynamischen "Daten" wie ein gemeinsames Objekt erzwingen könnten

@ahsonkhan ein weiteres Beispiel ist der Konfigurationsdienst in unserem Projekt. Es macht Sammlungen wie REST-Endpunkte verfügbar, die Sammlungen in MongoDB erstellen (es ist nicht nur ein Dummy-REST-Wrapper, und Restsammlungen und die Mongo-Sammlung haben keine exakte 1-1 Zuordnung, es gelten auch bestimmte Regeln).

In unserem Projekt benötigen wir also dynamische/ExpandoObject-Unterstützung.

Wir verwenden es auch in anderen Microservices.

Auch wir sind auf diese Einschränkung gestoßen. Unser Anwendungsfall baut nach und nach ein dynamisches Objekt vor der Json-Serialisierung auf. Zurück zum ausgereifteren Json.NET-Serializer.

Hallo Leute

Was ist der Lauf jetzt?
Darf ich konfigurieren, welche verwendet werden soll?
@SidShetye Sie sagten, dass etwas zu einem reiferen zurückgekehrt ist, können Sie es bitte erklären?

@MickeyReznikov , wurde deine Frage beantwortet? Ich glaube, @SidShetye wollte wieder Newtonsoft.Json zum Serialisieren dynamischer Objekte verwenden, da die In-Box-Bibliothek (System.Text.Json) diese noch nicht unterstützt. Im Fall von asp.net-Apps können Sie es auf AddNewtonsoftJson zurück konfigurieren.

Siehe https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#jsonnet -support

Ich habe auch einen Anwendungsfall dafür - ich möchte nur eine REST-API mit HttpClient aufrufen und eine einzelne Eigenschaft aus der Antwort abrufen. Ich möchte keine dedizierte Klasse erstellen, nur um die Antwort zu analysieren ... Mit JSON.NET könnte ich einfach "dynamisch" verwenden und auf die Eigenschaft der Wahl zugreifen.

Von https://github.com/dotnet/corefx/issues/41472 von @ghost1372 :

Hallo, ich hoffe, dies ist ein guter Ort, um diese Frage zu stellen
Es scheint, dass wir dynamische Objekte nicht deserialisieren können
Ich habe diesen Code verwendet, aber nicht funktioniert. Gibt es eine Möglichkeit, dies zu tun?

var objList = System.Text.Json.JsonSerializer.Deserialize<List<dynamic>>(json);

In unseren vielen vielen Anwendungen kontrollieren wir die Datenfelder aus den gespeicherten Prozeduren und rendern die Listen- und Suchlistenseiten dynamisch mit jquery.jtable

Ohne die integrierte JsonSerializer-Unterstützung für ExpandoObject können wir die in Dotnet Core integrierte Json Serialization nicht verwenden

Wir teilen den gleichen Anwendungsfall wie bereits von @estiller und @SidShetye erwähnt
Wir mussten in der Zwischenzeit wieder auf Json.NET umsteigen.

Ist es möglich, einen Meilenstein näher an _jetzt_ als an der Zukunft zu haben ? 🤔

ExpandoObject ist schon lange in der BCL

Gibt es eine Alternative zu ExpandoObject?

@fatihyildizhan Nein, es gibt keine Alternative.

aber wir haben unseren eigenen ExpandoObject-Konverter geschrieben, Sie können den Hinweis aus diesem Artikel entnehmen ASP.NET Core 3.0: Custom JsonConverter for the new System.Text.Json

Wir brauchen nur die Serialisierung, also erstellen wir nur die Serialisierung

Ich war überrascht, dass dies noch nicht unterstützt wird.

Verschieben Sie dies auf 5.0.

Auf 5.0 umsteigen? Das heißt, wir müssen mindestens ein Jahr warten? Zurück zu JSON.Net ist es.

5,0? wow, das ist definitiv scheiße.

Für eine vorübergehende hässliche Problemumgehung kann ich JsonDocument mit einem benutzerdefinierten Konverter dafür verwenden, aber es ist IDisposable.

public sealed class EventObject
    {
        [JsonPropertyName("id")]
        public long Id
        {
            get; set;
        }

        [JsonPropertyName("eventData")]
        [JsonConverter(typeof(JsonDocumentConverter))]
        public System.Text.Json.JsonDocument EventData
        {
            get; set;
        }
    }

    internal sealed class JsonDocumentConverter
        : JsonConverter<System.Text.Json.JsonDocument>
    {
        public override JsonDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return JsonDocument.ParseValue(ref reader);
        }

        public override void Write(Utf8JsonWriter writer, JsonDocument value, JsonSerializerOptions options)
        {
            value.WriteTo(writer);
        }
    }

Wir können die folgende POST-Aktion in Gegenwart von System.Text.Json nicht mehr verwenden:

[HttpPost]
public async Task<IActionResult> SubmitAsync(dynamic model)

Stattdessen mussten wir die folgende Methode verwenden, aber es gab keine einfache Möglichkeit, "Modell" im nachgelagerten Backend-Code zu verwenden:

[HttpPost]
public async Task<IActionResult> SubmitAsync(JsonElement model)

'Modell' ist ein komplexes Objekt und kann eine Sammlung von Objekten und/oder anderen verschachtelten komplexen Objekten enthalten. Um aus JsonElement ein dynamic Objekt schließen zu können, mussten wir model.GetRawText() aufrufen und von Newtonsoft in ein dynamisches Objekt dekodieren lassen. Dieser Weg ist nicht der gewünschte Weg, da der Hauptzweck dieser Übung darin bestand, Newtonsoft.json aus dem Projekt zu entfernen.

Kann ich davon ausgehen, dass das Beheben dieses Tickets/Problems eine Lösung für unser Problem impliziert, das wir erlebt haben? Es scheint ein dringend zu lösendes Problem zu sein, also kann es früher oder später angegangen werden?

/cc @ahsonkhan @terrajobst

.NET Core 3.0 JsonSerializer.Deserialize to dynamisches Objekt

JsonSerializer-Unterstützung für ExpandoObject (Vorläufige Maßnahmen)
Ich bin Neuling, viele Orte sind nicht perfekt, begrüßen Sie alle zum Ändern
.net Core3 keine Unterstützung

Fügen Sie den Json-Konverter hinzu

hinzufügen mit:

  • Verwenden von System.Text.Json;
  • Verwenden von System.Text.Json.Serialization;

```C#
///


/// Temp-Dynamischer Konverter
/// von:[email protected]
///

öffentliche Klasse DynamicJsonConverter : JsonConverter
{
public override dynamic Read(ref Utf8JsonReader reader,
Geben Sie typeToConvert ein,
JsonSerializerOptions-Optionen)
{

        if (reader.TokenType == JsonTokenType.True)
        {
            return true;
        }

        if (reader.TokenType == JsonTokenType.False)
        {
            return false;
        }

        if (reader.TokenType == JsonTokenType.Number)
        {
            if (reader.TryGetInt64(out long l))
            {
                return l;
            }

            return reader.GetDouble();
        }

        if (reader.TokenType == JsonTokenType.String)
        {
            if (reader.TryGetDateTime(out DateTime datetime))
            {
                return datetime;
            }

            return reader.GetString();
        }

        if (reader.TokenType == JsonTokenType.StartObject)
        {
            using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
            return ReadObject(documentV.RootElement);
        }
        // Use JsonElement as fallback.
        // Newtonsoft uses JArray or JObject.
        JsonDocument document = JsonDocument.ParseValue(ref reader);
        return document.RootElement.Clone();
    }

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
    {
        object? result = null;
        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.Object:
                result = ReadObject(jsonElement);
                break;
            case JsonValueKind.Array:
                result = ReadList(jsonElement);
                break;
            case JsonValueKind.String:
                //TODO: Missing Datetime&Bytes Convert
                result = jsonElement.GetString();
                break;
            case JsonValueKind.Number:
                //TODO: more num type
                result = 0;
                if (jsonElement.TryGetInt64(out long l))
                {
                    result = l;
                }
                break;
            case JsonValueKind.True:
                result = true;
                break;
            case JsonValueKind.False:
                result = false;
                break;
            case JsonValueKind.Undefined:
            case JsonValueKind.Null:
                result = null;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        return result;
    }

    private object? ReadList(JsonElement jsonElement)
    {
        IList<object?> list = new List<object?>();
        foreach (var item in jsonElement.EnumerateArray())
        {
            list.Add(ReadValue(item));
        }
        return list.Count == 0 ? null : list;
    }
    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
    {
       // writer.WriteStringValue(value.ToString());
    }
}
## How to Use?

```C#
var serializerOptions = new JsonSerializerOptions
{
    Converters = { new DynamicJsonConverter() }
};
return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

@tchivs , Ihre Lösung hat bei mir funktioniert; aber da die Converters-Eigenschaft schreibgeschützt ist, musste ich Folgendes tun:
c# var serializerOptions = new JsonSerializerOptions(); serializerOptions.Converters.Add(new DynamicJsonConverter()); return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

Versuchen Sie es mit dem JsonElement-Typ:

public JsonElement MyProperty {get; set;}

@tchivs , ich habe einige Änderungen an Ihrem Code vorgenommen - ihn so überarbeitet, dass er dynamisch generierte "Projektions" -Typen (mit einer Teilmenge von Eigenschaften eines Basistyps) anstelle von ExpandoObject verwendet. Ich habe den Code (in einem Beispiel-Konsolenprojekt) hier gepostet :

Ich werde diesen Ansatz mit komplizierteren Objekten und in verschiedenen Szenarien testen (z. B. Deserialisierung dynamischer Objekte, die zum Patchen vorhandener, vollständig typisierter EF Core-Entitäten verwendet werden; Deserialisierung von Json-Objekten und Arrays für Testfälle). Lassen Sie es mich wissen, wenn Sie dies nützlich finden oder wenn Sie etwas problematisches sehen.

Danke für den Community-Workaround. Es ist überraschend, dass Microsoft diese Funktion nicht in einem vernünftigen Zeitrahmen bereitstellen kann. Die Arbeit mit GraphQL-Antworten ohne irgendeine Art dynamischer Objekte führt zu viel ausführlichem und hässlichem Code. Oder einfach nur prüfen, ob tief verschachtelte Eigenschaften vorhanden sind.

Ich habe den Kommentarthread durchgelesen und die meisten konzentrieren sich auf die Deserialisierung. Ich stehe vor einem Problem, bei dem auch die Serialisierung dynamischer Objekte anscheinend stillschweigend "fehlschlägt". Um mein Szenario zu reproduzieren, habe ich mir die folgende minimale Reproduktion ausgedacht:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text.Json;

namespace SampleConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new CustomDynamicObject();
            d.AProperty = 10;
            var s = JsonSerializer.Serialize(d);

            Console.WriteLine(s);
            Console.Read();
        }
    }

    class CustomDynamicObject : DynamicObject 
    {
        private readonly IDictionary<string, object> properties = new Dictionary<string, object>();

        public override bool TryGetMember(GetMemberBinder binder, out object result) => properties.TryGetValue(binder.Name, out result);

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            properties[binder.Name] = value;
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            foreach (var item in properties.Keys)
            {
                yield return item;
            }
        }
    }
}

Wenn s ist {} , sodass die Serialisierung nicht fehlschlägt, sondern ein leeres Json-Objekt ergibt.

Ist das das richtige Thema? oder sollte ich einen anderen anheben/folgen?

Ist das das richtige Thema? oder sollte ich einen anderen anheben/folgen?

Dies ist das richtige Thema. Es ist nicht erforderlich, eine für die beiden Hälften derselben Funktion zu öffnen. Bei diesem Problem geht es darum, Unterstützung für Expando-Objekte hinzuzufügen (was für beide Seiten bedeutet, serialisieren und deserialisieren).

Ich habe dieses Problem heute kennengelernt - wollte sagen, dass JsonElement gut funktionieren würde, wenn es nicht von der Entsorgung von JsonDocument abhängt. Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, einen Destruktor für JsonDocument zu implementieren, damit seine Entsorgung auf einen späteren Zeitpunkt verschoben werden kann - sobald alle JsonElement Objekte gesammelt sind.

Ich brauchte auch ein dynamisches Objekt. Es ist jedoch noch nicht implementiert. Meine Lösung war, ein Wörterbuch zu verwenden.
JsonSerializer.Deserialize<Dictionary<string, string>>(response)
Und dann suche nach dem Schlüssel, den ich brauche :)

Nur für meine eigene Vernunft - ist das Problem für 5.0 speziell auf die Unterstützung von ExpandoObject oder die Fähigkeit zur Deserialisierung in ein dynamisches Objekt beschränkt? Die Konversation hier scheint nicht alle zum Titel der Ausgabe zu passen, und ich brauche definitiv letzteren. In meinem Fall deserialisiere ich eine verschachtelte Dynamik, insbesondere List<Dictionary<string, dynamic>> . Vorerst zurück zu Newtonsoft gewechselt :(

wollte nur einige c# 8 Syntax Sugar Mods von @tchivs hilfreichem Code

/// <summary>
/// Temp Dynamic Converter with c# 8
/// by:[email protected]
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
    public override dynamic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => reader.TokenType switch
        {
            JsonTokenType.True => true,
            JsonTokenType.False => false,
            JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),
            JsonTokenType.String => reader.TryGetDateTime(out DateTime datetime) ? datetime.ToString() : reader.GetString(),
            JsonTokenType.StartObject =>  ReadObject(JsonDocument.ParseValue(ref reader).RootElement),
            // Use JsonElement as fallback.
                _ =>JsonDocument.ParseValue(ref reader).RootElement.Clone()
        };

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
        =>
         jsonElement.ValueKind switch
        {
            JsonValueKind.Object => ReadObject(jsonElement),
            JsonValueKind.Array => ReadList(jsonElement),
            JsonValueKind.String => jsonElement.GetString(),
            JsonValueKind.Number =>  jsonElement.TryGetInt64(out long l) ? 1 :0,
            JsonValueKind.True => true,
            JsonValueKind.False =>false,
            JsonValueKind.Undefined => null,
            JsonValueKind.Null => null,
                _ => throw new ArgumentOutOfRangeException()
        };

    private object? ReadList(JsonElement jsonElement)
    {
        var list = new List<object?>();
        jsonElement.EnumerateArray().ToList().ForEach(j => list.Add(ReadValue(j)));
        return list.Count == 0 ? null : list;
    }

    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
        {
        // writer.WriteStringValue(value.ToString());
        }
}

@rs38 danke für den Code hier, war genau das, was ich brauchte. Wollte auf eine sehr subtile, aber wichtige Änderung hinweisen. Die beiden Zeilen, die den Typ "Zahl" analysieren, sind in Ihrer komprimierten Version falsch:

JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),

sollte sein

JsonTokenType.Number => reader.TryGetInt64(out long l) ? l : reader.GetDouble(),

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? 1 :0,

sollte sein

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? l :0,

@layomia Das ist nicht ernst. Der Support hätte schon vor langer Zeit erfolgen sollen (eigentlich hätte System.Text.Json meiner Meinung nach nicht ohne ihn gestartet werden dürfen)! Und wir haben nicht einmal eine Deadline!

In meinem Szenario geht es um CosmosDB. Ich frage mit dem neuen .NET SDK ab, das diesen JsonSerializer verwendet, und da es sich um eine schemalose Datenbank handelt, möchte ich nicht für jede Projektion der Datenbankdaten, die ich durchführen muss, eine Klasse erstellen (es gibt eine Reihe verschiedener Abfragen). .
Ich benötige die Ergebnisse der Abfragen als Listen dynamischer Objekte.

@SocVi100 Setzen Sie Ihre Hoffnungen nicht auf Microsoft. Bleiben Sie besser bei Json.net von Newtonsoft. @layomia hat alle Hoffnungen auf eine zunichte gemacht . Es scheint, dass sie sich nicht wirklich für die Bedürfnisse der Entwickler interessieren. Zuerst riefen sie "Vergiss json.net, wir haben dich abgedeckt!" Glücklicherweise haben die Entwickler json.net oder ähnliches nicht vergessen.

warum trägst du nicht bei? implementieren und in einer PR platzieren!

https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/adding-api-guidelines.md
https://github.com/dotnet/runtime/blob/master/docs/area-owners.md (Leitung:
@ericstj |, Inhaber: @layomia @steveharter @jozkee)

warum trägst du nicht bei? implementieren und in einer PR platzieren!

Ich wünschte, ich könnte.

In meinem Szenario geht es um CosmosDB. Ich frage mit dem neuen .NET SDK ab, das diesen JsonSerializer verwendet, und da es sich um eine schemalose Datenbank handelt, möchte ich nicht für jede Projektion der Datenbankdaten, die ich durchführen muss, eine Klasse erstellen (es gibt eine Reihe verschiedener Abfragen). .
Ich benötige die Ergebnisse der Abfragen als Listen dynamischer Objekte.

Es ist möglich, mit CosmosClientBuilder.WithCustomSerializer einen anderen Serializer zu konfigurieren.

Hier ist ein Beispiel: CosmosJsonNetSerializer

Wir konnten diese Funktion nicht in 5.0 integrieren. Wir mussten zusammen mit dem Rest der 5.0-Feature-Arbeit Prioritäten setzen, und dieser passte nicht. FWIW war dies sehr nahe an der Schnittlinie, also der späte Umzug.

@layomia hat alle Hoffnungen auf eine zunichte gemacht . Es scheint, dass sie sich nicht wirklich für die Bedürfnisse der Entwickler interessieren. Zuerst riefen sie "Vergiss json.net, wir haben dich abgedeckt!" Glücklicherweise haben die Entwickler json.net oder ähnliches nicht vergessen.

@RobbyDeLaet können wir versuchen, diese Diskussion konstruktiv zu halten? Wir versuchen unser Bestes zu tun, um auf die am häufigsten angeforderten Kundenfunktionen zu reagieren und gleichzeitig die Designprinzipien beizubehalten. In Bezug auf die Feature-Parität mit Newtonsoft.Json haben wir folgendes zu sagen .

System.Text.Json konzentriert sich hauptsächlich auf Leistung, Sicherheit und die Einhaltung von Standards. Für einige Szenarien verfügt System.Text.Json über keine integrierte Funktionalität, es gibt jedoch empfohlene Problemumgehungen. Wenn Ihre Anwendung von einem fehlenden Feature abhängt, sollten Sie in Erwägung ziehen, ein Problem einzureichen, um herauszufinden, ob Unterstützung für Ihr Szenario hinzugefügt werden kann.

Unser Ziel ist es nicht, Newtonsoft.JSON zu ersetzen. Wenn es für Sie funktioniert, verwenden Sie es weiter. Wir werden unser Bestes tun, um System.Text.Json so nützlich wie möglich zu machen und gleichzeitig die Fortschritte zu erhalten, die wir in Bezug auf Leistung, Sicherheit, Standardkonformität und Schichtung erzielt haben. Im Laufe der Zeit hoffen wir, dass es für so viele Leute wie möglich funktioniert. Ich höre hier das Interesse und werde dafür sorgen, dass wir uns auf diese Weiterentwicklung konzentrieren.

Danke für deine Vorschläge. Ich bin endlich mittendrin und verwende den Newtonsoft-Serializer nur für die Deserialisierung, aber ich muss noch testen. Ich hoffe, es dauert nicht zu lange, bis ExpandoObject-Unterstützung auf dem Standard-Serializer von CosmosDB erhalten wird, damit ich den alten loswerden kann.
@RobbyDeLaet , danke für deinen Kommentar! Ich habe keine großen Erwartungen an die Implementierung von Microsoft, ich habe mich zu oft darüber geärgert gefühlt. Als freiberuflicher Entwickler habe ich buchstäblich jahrelang darauf gewartet, dass wesentliche Funktionen in CosmosDB, EntityFramework und Identity-Infrastruktur implementiert werden, und sie gehen ihren eigenen Weg und ignorieren uns. Es spielt keine Rolle, wie viele Stimmen sie auf der UserVoice oder was auch immer haben. Ich vermute, dass solche Funktionen für eigene Entwicklungen nicht benötigt werden und daher außerhalb des Umfangs liegen...
Das Problem ist immer dasselbe: Zu viel Marketing = zu viel Erwartungen

Um denjenigen zu helfen, die diese Funktionalität benötigen, sehen Sie sich die obigen Codebeispiele zum Entsperren an:

  • Von @tchivs (das ExpandoObject für Objekte und List<object> für Sammlungen verwendet)
  • Von @denmitchell (das IL Emit für Objekte anstelle von ExpandoObject )

Um die Anforderungen für diese Funktion zu erfüllen, werde ich ein neues benutzerdefiniertes Konverter-Beispiel bereitstellen und dieses hier verlinken, wenn es fertig ist. Danach werde ich dieses Beispiel dem Newtonsoft-Workaround-Abschnitt hinzufügen . cc @tdykstra

Ein noch nicht erörtertes Detail ist, dass die Unterstützung von dynamic einen Verweis auf die sehr große System.Linq.Expressions.dll Assembly erfordert; Diese Komplikation kann bedeuten, dass wir in Zukunft den dynamischen Konverter in einer neuen Assembly hinzufügen (z. B. System.Text.Json.Converters.dll ), um Pay-to-Play für diejenigen zu erhalten, denen die Bereitstellungsgröße wichtig ist (z. B. eine eigenständige App oder ein Blazor). Client-App).

Nur für meine eigene Vernunft - ist das Problem für 5.0 speziell auf die Unterstützung von ExpandoObject oder die Fähigkeit zur Deserialisierung in ein dynamisches Objekt beschränkt? Die Konversation hier scheint nicht alle zum Titel der Ausgabe zu passen, und ich brauche definitiv letzteren. In meinem Fall deserialisiere ich eine verschachtelte Dynamik, insbesondere List<Dictionary<string, dynamic>> . Vorerst zurück zu Newtonsoft gewechselt :(

Ich teste jetzt eine alte Funktion, die ich zum Deserialisieren verschachtelter Objekte implementiert habe, und sehe das gleiche Problem, das in diesem Thema mit "valueKind" beschrieben wird.

Gab es eine Möglichkeit, dies mit den neuen neetonsoft.json-Paketen zu beheben? Weil der obige Code für das Root-Level-Objekt funktioniert hat, aber nicht für die verschachtelten Objekte.

@ericstj Entschuldigung, meine Reaktion war vielleicht etwas zu negativ und hart, aber wie @SocVi100 bin ich ein freiberuflicher Entwickler und manchmal

Nur für meine eigene Vernunft - ist das Problem auf 5.0 speziell auf die Unterstützung von ExpandoObject oder die Fähigkeit zur Deserialisierung in ein dynamisches Objekt beschränkt?

Ich gehe davon aus, dass die gewünschte Semantik einen dynamischen Typ haben soll, sodass Sie nach der Deserialisierung auf alle Eigenschaften (einschließlich verschachtelter) spät zugreifen können:

dynamic obj = JsonSerializer.Deserialize<dynamic>(json);
string s = obj.MyChildProperty.MyStringList[2];

und ich gehe davon aus, dass die gewünschte Semantik auch ExpandoObject explizit unterstützt:

ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
dynamic obj = expando;
string s = obj.MyChildProperty.MyStringList[2];

Also mein Verständnis des Umfangs:

  • Deserialize<dynamic>(json) gibt entweder ein Primitiv, eine Sammlung oder ein Objekt zurück. Ein Objekt hier wird wahrscheinlich ExpandoObject , könnte aber je nach Implementierung IDynamicMetaObjectProvider oder ein JITed-Typ sein. Dies ist anders als heute, das immer JsonElement zurückgibt.
  • Deserialize<ExpandoObject>(json) (welches IDynamicMetaObjectProvider implementiert) sollte ein richtiges ExpandoObject erzeugen, das wiederum mit dynamic . Dies ist anders als heute, wo nur Expando-Eigenschaften für die Root-Eigenschaften und JsonElement Instanzen für alle verschachtelten Eigenschaften erstellt werden.
  • Serialize<ExpandoObect>() funktioniert wie erwartet
  • Serialize<IDynamicMetaObjectProvider ()> kann implementiert werden oder nicht, je nachdem, ob es Szenarien gibt, in denen ExpandoObject nicht verwendet wird.

Semantik in 3.0 - 5.0:

  • Das Serialisieren eines ExpandoObject funktioniert, da ExpandoObject IDictionary<string, object> ExpandoObject implementiert und STJ jeden object Wert korrekt serialisiert, egal ob es sich um ein Primitiv, eine Sammlung oder ein Objekt handelt.
  • Das Deserialisieren in eine ExpandoObject Art funktioniert, aber nur die Root-Eigenschaften sind "richtige" Expando-Eigenschaften; alle verschachtelten Eigenschaften sind JsonElement daher gibt es Inkonsistenzen im Programmiermodell, daher sollte eine Deserialisierung von ExpandoObject vermieden werden, es sei denn, ein benutzerdefinierter Konverter wird dafür verwendet.

Optionen für 3.0-5.0:

  • Schreiben Sie einen benutzerdefinierten Konverter für ExpandoObject und\oder object wie in den obigen Beiträgen erwähnt; Ich werde in Kürze einen Link zu einem neuen Beispiel bereitstellen, das idealerweise einer 6.0-Funktion zugeordnet ist.
  • Wenn Sie nur serialisieren, nicht deserialisieren, können Sie ExpandoObject (oder dynamic wenn der Typ auf ExpandoObject basiert). Da beim Deserialisieren von ExpandoObject heute ein Inkonsistenzproblem in STJ auftritt, wird ExpandoObject beim Deserialisieren nicht empfohlen.
  • Verwenden Sie JsonElement anstelle von dynamischen Objekten. JsonElement versucht nicht zu "raten", welchem ​​Typ die JSON-Zuordnung zugeordnet ist, daher müssen Sie explizit sein, indem Sie GetString(), GetInt32() usw. aufrufen.

Abhängig von der Implementierung bedeutet die Implementierung eines benutzerdefinierten Konverters wahrscheinlich auch, dass es einige "Erraten" gibt, welchen CLR-Typen das JSON während der Deserialisierung zugeordnet wird. Beispielsweise kann ein JSON-String entweder einem DateTime oder einem string , eine JSON-Zahl kann einem double oder einem long , und die Der JSON-Array-Typ muss bestimmt werden. Dies muss überprüft und mit der Newtonsoft-Semantik verglichen werden. Dies sollte sich auch daran orientieren, welcher Typ zurückgegeben wird, wenn eine Eigenschaft vom Typ object (heute ist es JsonElement ).

Hier sind einige Beispiele für die 3.0 - 5.0 STJ-Semantik:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;

namespace ConsoleApp
{
    class Program
    {
        const string ExpectedJson = "{\"A\":\"A\",\"B\":[1,2],\"C\":42,\"D\":\"2020-01-01T00:00:00\",\"E\":{\"A_Child\":\"A_Child\"}}";

        static void Main(string[] args)
        {
            DateTime dateTime = new DateTime(2020, 1, 1);

            dynamic myDynamicChild = new ExpandoObject();
            myDynamicChild.A_Child = "A_Child";

            dynamic myDynamic = new ExpandoObject();
            myDynamic.A = "A";
            myDynamic.B = new List<int>() { 1, 2 };
            myDynamic.C = 42;
            myDynamic.D = dateTime;
            myDynamic.E = myDynamicChild;

            // Verify we can call late-bound property.
            int c = myDynamic.C;
            Debug.Assert(c == 42);

            // STJ can serialize with ExpandoObject since it implements IDictionary<string, object>.
            string json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Using 'dynamic' against backing ExpandoObject works.
            json = JsonSerializer.Serialize<dynamic>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Deserialize with <dynamic>, <object> and <JsonElement>.
            // For 5.0, using one of these is recommended over ExpandoObject because the programming model will be
            // consistent for the root type and all nested types.
            // Using <JsonElement> makes it clear and non-abiguous.
            // Using <object> by default uses 'JsonElement', but can be overridden by a custom converter.
            // Using <dynamic> uses 'object' which uses 'JsonElement'.
            {
                dynamic d = JsonSerializer.Deserialize<dynamic>(json);
                VerifyJsonElement(d);

                try
                {
                    // We will get an exception here if we try to access a dynamic property since 'object' is deserialized
                    // as a JsonElement and not an ExpandoObject.
                    c = d.C;
                    Debug.Fail("Should have thrown Exception!");
                }
                catch (Exception ex)
                {
                    Debug.Assert(ex.Message == "'System.Text.Json.JsonElement' does not contain a definition for 'C'");
                }

                // Serializing with <object> creates a JsonElement by default (can be changed by a custom converter).
                object o = JsonSerializer.Deserialize<object>(json);
                VerifyJsonElement((JsonElement)o);

                // Serialize with explicit <JsonElement>.
                JsonElement e = JsonSerializer.Deserialize<JsonElement>(json);
                VerifyJsonElement(e);
            }

            // Deserialize with ExpandoObject. This creates an ExpandoObject with the root Type having Expando-properties
            // but the value of those properties will be JsonElement. All other nested properties\objects\collections will
            // also be JsonElement. Due to the inconsistency of having only root-level Expando-properties (such as A_Child),
            // deserializing as ExpandoObject is not recommended (unless a ExpandoObject custom converter is used).
            {
                // When STJ deserializes, it creates ExpandoObjects via IDictionary<string, object> where 'object' is JsonElement.
                ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
                Debug.Assert(((IDictionary<string, object>)expando).Keys.Count == 5);
                myDynamic = expando;

                JsonElement jsonElement = myDynamic.A;
                Debug.Assert(jsonElement.GetString() == "A");

                jsonElement = myDynamic.B;
                Debug.Assert(jsonElement.EnumerateArray().Count() == 2);

                jsonElement = myDynamic.C;
                Debug.Assert(jsonElement.GetInt32() == 42);

                jsonElement = myDynamic.D;
                Debug.Assert(jsonElement.GetDateTime() == dateTime);

                jsonElement = myDynamic.E;
                // Here we have an inconsistency. Nested object property must use JsonElement (not a dynamic property).
                Debug.Assert(jsonElement.GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize works as expected.
                json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
                Debug.Assert(json == ExpectedJson);

                // Re-serialize works as expected; dynamic works here since backed by ExpandoObject in this example.
                json = JsonSerializer.Serialize<dynamic>(myDynamic);
                Debug.Assert(json == ExpectedJson);
            }

            void VerifyJsonElement(JsonElement elem)
            {
                // Verify JsonElement
                Debug.Assert(elem.GetProperty("A").GetString() == "A");
                Debug.Assert(elem.GetProperty("B").EnumerateArray().Count() == 2);
                Debug.Assert(elem.GetProperty("C").GetInt32() == 42);
                Debug.Assert(elem.GetProperty("D").GetDateTime() == dateTime);
                Debug.Assert(elem.GetProperty("E").GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize
                json = JsonSerializer.Serialize<dynamic>(elem);
                Debug.Assert(json == ExpectedJson);

                json = JsonSerializer.Serialize<JsonElement>(elem);
                Debug.Assert(json == ExpectedJson);
            }
        }
    }
}

@rs38 Können Sie Ihr Code-Snippet basierend auf dem reparieren, was @ryan-hollister-q2 herausgestellt hat?

Wie versprochen, finden Sie die PR mit einem dynamischen Implementierungsbeispiel unter https://github.com/dotnet/runtime/pull/42097.

@rs38 Können Sie Ihr Code-Snippet basierend auf dem reparieren, was @ryan-hollister-q2 herausgestellt hat?

das war nicht mein Code, habe nur einige Ausschnitte von @tchivs weiter oben in diesem Thread geteilt.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

v0l picture v0l  ·  3Kommentare

GitAntoinee picture GitAntoinee  ·  3Kommentare

jkotas picture jkotas  ·  3Kommentare

sahithreddyk picture sahithreddyk  ·  3Kommentare

chunseoklee picture chunseoklee  ·  3Kommentare