Runtime: 新しいAsp.NETCore 3.0Jsonは辞書をシリアル化しません<key/>

作成日 2019年08月07日  ·  51コメント  ·  ソース: dotnet/runtime

.NET Core3.0プレビュー7

Asp.NET Web Apis、辞書を返すと、NotSupportedExceptionで失敗します。 以下に例外を含めました。

また、 ControllerBase.BadRequestメソッドはModelStateDictionary受け取りますが、それが返されると、シリアライザーもNotSupportedExceptionで爆発しますが、メッセージは少し異なります。

このサポートはいつ追加されますか? これはJson.netや他のシリアライザーでしばらくの間サポートされているので、これが注目されていることを願っています。

Json.netの使用をオプトインできるという事実に感謝します。ありがとうございます!

辞書を返すときの例外
System.NotSupportedException:コレクションタイプ 'System.Collections.Generic.Dictionary`2 [System.Int32、System.String]'はサポートされていません。
System.Text.Json.JsonClassInfo.GetElementType(Type propertyType、Type parentType、MemberInfo memberInfo、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo.CreateProperty(タイプdeclaredPropertyType、タイプruntimePropertyType、PropertyInfo propertyInfo、タイプparentClassType、JsonSerializerOptionsオプション)
System.Text.Json.JsonClassInfo.AddProperty(Type propertyType、PropertyInfo propertyInfo、Type classType、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo.AddPolicyProperty(Type propertyType、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo..ctor(タイプタイプ、JsonSerializerOptionsオプション)で
System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type classType)で
System.Text.Json.WriteStackFrame.Initialize(タイプタイプ、JsonSerializerOptionsオプション)で
System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json、オブジェクト値、タイプタイプ、JsonSerializerOptionsオプション、CancellationToken cancelToken)で
Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context、Encoding selectedEncoding)で
Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context、Encoding selectedEncoding)で
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Logged | 21_0(ResourceInvoker呼び出し元、IActionResult結果)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Awaited | 29_0 TFilter、TFilterAsync
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)で
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter、TFilterAsyncで
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()で
---例外がスローされた前の場所からのスタックトレースの終わり---
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Awaited | 19_0(ResourceInvoker呼び出し側、タスクlastTask、状態next、スコープスコープ、オブジェクト状態、ブール値isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Logged | 17_1(ResourceInvoker呼び出し側)
Microsoft.AspNetCore.Routing.EndpointMiddlewareで。g__AwaitRequestTask | 6_0(エンドポイントエンドポイント、タスクrequestTask、ILoggerロガー)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)で
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)で
Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)で
Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)で
Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext、ISwaggerProvider swaggerProvider)で
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)で

BadRequestを返すときの例外
System.NotSupportedException:コレクションタイプ 'Microsoft.AspNetCore.Mvc.SerializableError'はサポートされていません。
System.Text.Json.JsonClassInfo.GetElementType(Type propertyType、Type parentType、MemberInfo memberInfo、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo.CreateProperty(タイプdeclaredPropertyType、タイプruntimePropertyType、PropertyInfo propertyInfo、タイプparentClassType、JsonSerializerOptionsオプション)
System.Text.Json.JsonClassInfo.AddProperty(Type propertyType、PropertyInfo propertyInfo、Type classType、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo.AddPolicyProperty(Type propertyType、JsonSerializerOptions options)で
System.Text.Json.JsonClassInfo..ctor(タイプタイプ、JsonSerializerOptionsオプション)で
System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type classType)で
System.Text.Json.WriteStackFrame.Initialize(タイプタイプ、JsonSerializerOptionsオプション)で
System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json、オブジェクト値、タイプタイプ、JsonSerializerOptionsオプション、CancellationToken cancelToken)で
Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context、Encoding selectedEncoding)で
Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context、Encoding selectedEncoding)で
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Logged | 21_0(ResourceInvoker呼び出し元、IActionResult結果)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Awaited | 29_0 TFilter、TFilterAsync
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)で
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter、TFilterAsyncで
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()で
---例外がスローされた前の場所からのスタックトレースの終わり---
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Awaited | 19_0(ResourceInvoker呼び出し側、タスクlastTask、状態next、スコープスコープ、オブジェクト状態、ブール値isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvokerで。g__Logged | 17_1(ResourceInvoker呼び出し側)
Microsoft.AspNetCore.Routing.EndpointMiddlewareで。g__AwaitRequestTask | 6_0(エンドポイントエンドポイント、タスクrequestTask、ILoggerロガー)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)で
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)で
Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)で
Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)で
Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext、ISwaggerProvider swaggerProvider)で
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)で

area-System.Text.Json enhancement

最も参考になるコメント

新規参入者の場合、一時的な解決策はNewtonsoft.Jsonに戻すことです。

  1. パッケージ参照をMicrosoft.AspNetCore.Mvc.NewtonsoftJson追加します。
  2. .AddControllers() / .AddMvc()またはその他の組み合わせの直後に.AddNewtonsoftJson()を追加します。

全てのコメント51件

両方の「サポートされていない例外」エラーは、組み込みシリアライザー内の制限であり、設計によるものです(少なくとも3.0で出荷されているものについて)。

このサポートはいつ追加されますか? これはJson.netや他のシリアライザーでしばらくの間サポートされているので、これが注目されていることを願っています。
Json.netの使用をオプトインできるという事実に感謝します。ありがとうございます!

vNext(つまり、5.0)以降でサポートするシリアライザー機能が多数あり、カスタム辞書のサポートもその1つです。

Asp.NET Web Apis、辞書を返すときにNotSupportedExceptionで失敗する

シリアル化する場合、現在はDictionary<string, TValue>がサポートされています(つまり、文字列であるTKey)。 お使いの辞書は<int, string>であり、サポートされていません。
https://github.com/dotnet/corefx/blob/93d7aa1c1737b6da29d04b78557215e18eb786d4/src/System.Text.Json/tests/Serialization/DictionaryTests.cs#L385 -L390

@ steveharter@ layomia-当面の間、ここに潜在的な回避策はありますか? 5.0のシリアライザー内で文字列以外のキー付き辞書のサポートを追加するには何が必要ですか?

System.NotSupportedException:コレクションタイプ 'Microsoft.AspNetCore.Mvc.SerializableError'はサポートされていません。

〜SerializableErrorのようなタイプのサポートを追加することは私のレーダーにはありませんでした。 @ pranavkm@ rynowak-ここでのコンテキストは何ですか? 私はModelStateDictionaryに精通していませんが、これはカスタムコンバーターを使用してmvc自体でサポートできますか?〜

編集:気にしないでください、それはすでに修正されています。

System.NotSupportedException:コレクションタイプ 'Microsoft.AspNetCore.Mvc.SerializableError'はサポートされていません。

これは既知の問題https://github.com/aspnet/AspNetCore/issues/11459で、最近修正されました(プレビュー8の一部として): https

迅速な返信@ahsonkhanをありがとうございました!

文字列であるキーの「制限」は、私がそれについて考えるとき、実際に理にかなっています。 Json.netが実際にjsonを生成し、キーが文字列であることがわかりました。逆シリアル化すると、intが返されます。 将来的に文字列以外のキーがサポートされるのは間違いなく素晴らしいことですが、ショーストッパーではありません。

わかりました。サポートされていないMvc.SerializableErrorが修正されたと聞いてうれしいです。 プレビュー8のリリース日を希望するかどうかについて何かアイデアはありますか? 何かを検索して見つけようとしましたが、それについては何も表示されませんでした。

プレビュー8が出たら、.netコア3のjsonシリアル化ライブラリをもう一度試してみますが、今のところはJson.netを使い続ける必要があります。

@ steveharter@ layomia-当面の間、ここに潜在的な回避策はありますか? 5.0のシリアライザー内で文字列以外のキー付き辞書のサポートを追加するには何が必要ですか?

>>

@ahsonkhan @ willyt150この回避策は、 JsonConverter<T>を実装するカスタムコンバーターを使用することです。ここで、 TDictionary<int, string>です。
いくつかの例については、 https: //github.com/dotnet/corefx/issues/36639#issue-429928740を参照して

プレビュー8のリリース日を希望するかどうかについて何かアイデアはありますか?

今月の後半。

これについてもう少し考えて、デフォルトでサポートしたくないものかもしれないので、今のところグラブを削除します。

ありがとう@layomia私はそれを調べます。

@ahsonkhanに感謝します。修正が

@namseから(https://github.com/dotnet/corefx/issues/40404から):

こんにちは。 辞書を整数キーでシリアル化しようとすると、System.NotSupportedExceptionがスローされます。

辞書にToStringなキーがあるJsonシリアル化をサポートすることは理にかなっていると思います。 たとえば、intまたはbooleanに対してToStringを実行すると、「123」または「true」が返されます。 キーはToStringキーだと思います。

ベリソン

System.Text.Json Nugetバージョン:4.6.0-preview8.19405.3

コード

var dictionary = new Dictionary<int, string>
{
  [5] = "five"
};
JsonSerializer.Serialize(dictionary);

期待される

"{"5": "five"}"

しかし、何が起こるか

エラーSystem.NotSupportedExceptionスローされました

実際、Newtonsoft.JsonをSystem.Text.Jsonに変更すると、互換性の問題が発生します。 期待どおりに文字列を返します。 System.Text.Jsonは互換性がある必要はないと思いますが、...ご存知のとおりです。

IDictionary<TKey, TValue>シリアル化と逆シリアル化の両方をサポートするコンバーターを実装しました。 TKeyには静的メソッドTKey Parse(string)ます。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonDictionaryConverter
{
    sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
    {
        public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var convertedType = typeof(Dictionary<,>)
                .MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
            var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
            var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
                typeToConvert,
                BindingFlags.Instance | BindingFlags.Public,
                null,
                null,
                CultureInfo.CurrentCulture);
            var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
            var parse = typeof(TKey).GetMethod("Parse", 0, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Any, new[] { typeof(string) }, null);
            if (parse == null) throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
            while (enumerator.MoveNext())
            {
                var element = (KeyValuePair<string?, TValue>)enumerator.Current;
                instance.Add((TKey)parse.Invoke(null, new[] { element.Key }), element.Value);
            }
            return instance;
        }

        public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
        {
            var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
            foreach (var (k, v) in value) convertedDictionary[k?.ToString()] = v;
            JsonSerializer.Serialize(writer, convertedDictionary, options);
            convertedDictionary.Clear();
        }
    }

    sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            if (!typeToConvert.IsGenericType) return false;
            if (typeToConvert.GenericTypeArguments[0] == typeof(string)) return false;
            return typeToConvert.GetInterface("IDictionary") != null;
        }

        public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>)
                .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);
            var converter = (JsonConverter)Activator.CreateInstance(
                converterType,
                BindingFlags.Instance | BindingFlags.Public,
                null,
                null,
                CultureInfo.CurrentCulture);
            return converter;
        }
    }
}

テスト:

class Entity
{
    public string Value { get; set; }
}
class TestClass
{
    public Dictionary<int, Entity> IntKey { get; set; }
    public Dictionary<float, Entity> FloatKey { get; set; }
    public Dictionary<double, Entity> DoubleKey { get; set; }
    public Dictionary<DateTime, Entity> DateTimeKey { get; set; }
    public Dictionary<string, Entity> StringKey { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var options = new JsonSerializerOptions();
        options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory());
        var x = new TestClass
        {
            IntKey = new Dictionary<int, Entity> { [1] = new Entity { Value = "test" } },
            FloatKey = new Dictionary<float, Entity> { [1.3f] = new Entity { Value = "test" } },
            DoubleKey = new Dictionary<double, Entity> { [1.35] = new Entity { Value = "test" } },
            DateTimeKey = new Dictionary<DateTime, Entity> { [DateTime.Now] = new Entity { Value = "test" } },
            StringKey = new Dictionary<string, Entity> { ["test"] = new Entity { Value = "test" } }
        };

        var value = JsonSerializer.Serialize(x, options);
        Console.WriteLine(value);
        var obj = JsonSerializer.Deserialize<TestClass>(value, options);
        Console.WriteLine(JsonSerializer.Serialize(obj, options));
    }
}

結果:

{"IntKey":{"1":{"Value":"test"}},"FloatKey":{"1.3":{"Value":"test"}},"DoubleKey":{"1.35":{"Value":"test"}},"DateTimeKey":{"8/25/2019 6:47:48 PM":{"Value":"test"}},"StringKey":{"test":{"Value":"test"}}}
{"IntKey":{"1":{"Value":"test"}},"FloatKey":{"1.3":{"Value":"test"}},"DoubleKey":{"1.35":{"Value":"test"}},"DateTimeKey":{"8/25/2019 6:47:48 PM":{"Value":"test"}},"StringKey":{"test":{"Value":"test"}}}

ただし、System.Text.Jsonは内部型Dictionary<int, int>受け入れないため、 Dictionary<int, Dictionary<int, int>>などのネストされた辞書をシリアル化することはできません。 バグだと思います。

ただし、Dictionaryなどのネストされた辞書をシリアル化することはできません。> System.Text.Jsonは内部型Dictionaryを受け入れないため。 バグだと思います。

<string, x>のみをサポートするのは、v3.0向けに出荷するためのスコープカットによる設計によるものです。 3.0リリースは、最も一般的なシナリオがサポートされた、最小限の実行可能な製品となることを目的としています。

@steveharter少なくとも、使用可能なコンバーターがある場合は、notsupportexceptionをスローしないでください。

.net Core 3.1でこれをサポートする計画はありますか?

新規参入者の場合、一時的な解決策はNewtonsoft.Jsonに戻すことです。

  1. パッケージ参照をMicrosoft.AspNetCore.Mvc.NewtonsoftJson追加します。
  2. .AddControllers() / .AddMvc()またはその他の組み合わせの直後に.AddNewtonsoftJson()を追加します。

@steveharter少なくとも、使用可能なコンバーターがある場合は、notsupportexceptionをスローしないでください。

はい、それは公正な点です。 おそらく、3.1のこの制限を取り除くことができます。 cc @layomia

また、今日の辞書要素は、キーが文字列であるために可能なプロパティのようにシリアル化されていることを明確にするためです。 文字列以外のキーをサポートするということは、要素がKeyValuePairとしてシリアル化されることを意味します。

ああ、3.0にアップグレードした直後にこの問題が発生しました。

AddNewtonsoftJsonを使用してnewtonパッケージをインストールする必要がありました。

https://github.com/dotnet/corefx/issues/41345の@israellotから

var dictionary = new Dictionary<int, int>()
            {
                [0] = 1
            };

 var serialized = System.Text.Json.JsonSerializer.Serialize(dictionary);

この単純なシリアル化は、intキーを文字列としてシリアル化することにより、以前のデフォルトのNewtonsoftjsonライブラリによって適切に処理されます。 System.Text.Jsonで、サポートされていない例外がスローされます。

スレッドの@ israellot@ unruledboyなど、シナリオでオブジェクトモデルに整数キーの辞書が必要な理由と、それをDictionary<string, TValue>変更しても機能しない理由について詳しく教えてください。 要件を収集し、修正の動機付けに役立ついくつかの使用法を見てみたいと思います。

とにかくそれらは文字列としてシリアル化されるので、どのシナリオで基礎となる辞書に代わりにint32キーを持たせたいのかわかりません。

@ahsonkhan主な動機は互換性だと思います。
以前のデフォルトのシリアライザーはNewtonsoftであったため、ユーザーは任意のクラスをシリアル化および逆シリアル化する機能に依存してモデル全体を作成した可能性があります。 2.Xから3.0に移行すると、実行時の非互換性しかわからないため、サイレントブレークの変更が発生します。

多くのシナリオでは、ネットワークを介したトランスポートと同じようにjsonを使用する必要があると思います。この場合、ドメインモデルはDictionaryである可能性があります。。 あなたの提案は、別のDTOオブジェクト辞書を作成することに要約されますシリアライザーに準拠するためだけに別のオブジェクトを割り当てる必要があるため、2つの間での変換はかなり非効率的です。
シリアライザーに厳密に目を向けると、文字列キーを持つように辞書を制限することは論理的です。それが唯一の可能なjson表現だからです。 しかし、シリアライザーがアプリケーションで役割を果たすことを考えると、できるだけ多くの摩擦を取り除くことが最善だと思います。

私が書いている小さなプログラムでこの問題に遭遇しました。バージョンラベルの一部がjsonファイルを介して提供されています。
ラベルパーツには、ラベルパーツを挿入できるインデックスを指定するキーがあります。 これは、キーが数値であることを意味します。

{
  "parts" : {
    "1" : "alpha",
    "3" : "beta"
  }
}

Newtonsoftを使用すると、jsonを問題なくDictionary<int, string>逆シリアル化できます。 System.Text.Jsonに変換した後、シリアル化に失敗しました。

独自のDictionaryConverterDictionaryConverterを作成することで、この問題を解決しました。
また、整数を文字列から逆シリアル化できる単純なコンバーターを作成しました

これらは、シリアライザーオプションを介して登録されます:https://github.com/Kieranties/SimpleVersion/blob/master/src/SimpleVersion.Core/Serialization/Serializer.cs#L22

これらの変更により、辞書のキーを文字列として直接読み取るのではなく、逆シリアル化することができます。 これにより、キーのサポートがさらに開かれ、独自のコンバーターをシリアル化用に登録できる任意のタイプにすることができます(たとえば、一意のキーとしてシリアル化できる列挙型/タイプ/タイプなど)。

私は正式にテストを行っていませんが、現在の開発ではこれで問題が解決したようです。

3.1のマイルストーンを設定して、 Dictionary<TKey,TValue> TKeyを処理できるカスタムコンバーターの作成を妨げる制限を削除します。

更新:3.0で動作するサンプルを追加しました。 上記のようにネストされた辞書などの問題には気づきませんでした。

サンプルで使用されているJSON形式は2つあります。

  • 文字列プロパティを持つJSONオブジェクト: {"1":"val1","2":"val2"} TKeyは文字列ではありませんが、 {"1":"val1","2":"val2"}です。

    • これらは、対応するサポートされているキータイプのTryParseメソッドに依存しています。 v3.0の場合、 TryParseメソッドはどのTKeyでも異なる可能性があり、提供する必要のあるカルチャ設定があるため、一般化されたTKeyサポートを提供することはできません。 (通常は不変)。 したがって、以下のサンプルは単一のTKeyタイプ用です。

    • Sample Dictionary<int, string> 。 これは、 TValue == string簡単な例です。

    • Sample Dictionary<Guid, TValue> 。 これは、任意のTValueを処理できます。

    • Sample Dictionary<TKey, TValue> where TKey is Enum 。 列挙型の場合; これは任意のTValueを処理できます。

  • KeyValuePairエントリを含むJSON配列: [{"Key":1,"Value":"val1"},{"Key":2,"Value":"val2"}]

これらのサンプルで問題がなければ、この問題を5.0に変更して、カスタムコンバーターを必要としない組み込みのサポートを提供するかどうかについて説明します。

検討のためにマイルストーンを5.0に設定します(上記の例のいずれかがデフォルトで機能する場合はどうなりますか)。

デシリアライズは、ジェネリックオブジェクトタイプを通常の(プリミティブ?)タイプではなくJsonDocumentにマップするようにも見えます。

例:

string test = "[{\"id\":86,\"name\":\"test\"}]";
var SystemTextJson = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, object>>>(test);
var NewtonSoftJson = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(test);

SystemTextJson [0] ["id"]は次のように表示されます:ValueKind = Number: "86"
NewtonSoftJson [0] ["id"]は次のように表示されます:86

@steveharterその
{ "Stuff": [ { "Key": 1, "Value": "String" }, { "Key": 3, "Value": "String" }, { "Key": 2, "Value": "String" } ] }

JSON.NETは、ほとんどの人が期待するものを提供しますが、次のようになります。
{ "Stuff": { "Item1": "String", "Item2": "String", "Item3": "String" }

デシリアライズは、ジェネリックオブジェクトタイプを通常の(プリミティブ?)タイプではなくJsonDocumentにマップするようにも見えます。

はい、それは仕様によるものです。 https://github.com/dotnet/corefx/issues/38713を参照して

@steveharterその
{"Stuff":[{"Key":1、 "Value": "String"}、{"Key":3、 "Value": "String"}、{"Key":2、 "Value": "弦" } ] }
JSON.NETは、ほとんどの人が期待するものを提供しますが、次のようになります。
{"Stuff":{"Item1": "String"、 "Item2": "String"、 "Item3": "String"}

「サンプル辞書」を使用していると思います「?そうであれば、「Key」プロパティと「Value」プロパティを持つKeyValuePairを使用しました。プロパティ名としてシリアル化されるTKeyベースの列挙型ディクショナリのサンプルは提供しませんでしたが、追加に取り組みます。

うん、あれ。 そして、ああ、私はあなたがそれを一般的な辞書シリアライザーとして意味していたと思いました。
現在使用しているサンプルは、私が望んでいるほど速くはないようですので、利用可能になったときに新しいサンプルを確認することに興味があります。

@roguecodeはDictionary<TKey, TValue>列挙型サンプルです。TKeyは列挙型であり、KeyValuePairの代わりに「プロパティ」JSON構文を使用します。 また、上記のサンプルのリストを更新して、この新しいサンプルを含めました。

こんにちは、似ていますが違うものがあります。他にどこを見ればいいのか教えていただけませんか。

Newtonsoft.Json:12.0.2
Microsoft.AspNetCore.Mvc.NewtonsoftJson:3.0.0

3.0として実行されているスタートアップ:

                .AddMvc(mvcOptions => mvcOptions.EnableEndpointRouting = false)
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddNewtonsoftJson();

このproblemDetailsオブジェクトがある場合:

// problemDetails has 2 extension items
{Microsoft.AspNetCore.Mvc.ValidationProblemDetails}
    Detail: "Please refer to the 'errors' property for additional details."
    Errors: Count = 1
    Extensions: Count = 2
    Instance: "/api/test/complex-binding-from-query"
    Status: 400
    Title: "One or more validation errors occurred."
    Type: "https://tools.ietf.org/html/rfc7231#section-6.5.1"

2.2では、 Newtonsoft.Json.JsonConvert.SerializeObject(problemDetails)実行すると

{"errors":{"Int":["The value 'a' is not valid for Int."]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"detail":"Please refer to the 'errors' property for additional details.","instance":"/api/test/complex-binding-from-query","traceId":"0HLQQ40AFBJNG","correlationId":"0HLQQ40AFBJNG"}

3.0では、次のようになります。

{"Errors":{"param":["The value 'a' is not valid."]},"Type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","Title":"One or more validation errors occurred.","Status":400,"Detail":"Please refer to the 'errors' property for additional details.","Instance":"/bad-request","Extensions":{"traceId":"|d0d40f80-48b6c9184401b0e1.","correlationId":"0HLQR10NSMRGD:00000009"}}

3.0バージョンのシリアル化された文字列にはIDictionaryが含まれていますプロパティ名Extensionsであり、3.0でその文字列を正しく逆シリアル化できます。 このプロパティ名が2.xバージョンから除外されていることがわかります。

問題は、以下のコードから、 BadRequestObjectResultを使用してフィルターから応答が返されるときに発生する3.0シリアル化です。

public sealed class ProblemDetailsResultFilterAttribute : Attribute, IAlwaysRunResultFilter
{
        public void OnResultExecuting(ResultExecutingContext context)
        {
             context.Result = new BadRequestObjectResult(problemDetails);
        }
}

...、返される応答コンテンツは2.2バージョンと同じ形式です( Extensionsプロパティ名は除外されます)。これにより、 Extensionsプロパティが空のコレクションとして逆シリアル化されます(を使用してNewtonsoft.Json.JsonConvert.DeserializeObject<ValidationProblemDetails>()

どういうわけか、このシリアル化は、逆シリアル化しようとしているNewtonsoftライブラリと同じシリアル化を使用していません。

検討してくれてありがとう!

私は似ているが違うものを持っています

@ ts46235これは現在の問題とは異なるので、これについて新しい問題を開いて

@ ts46235ここで開いた他の問題( https://github.com/aspnet/AspNetCore/issues/16618)であなたの質問に回答しました

Core 3.1に更新されましたが、まだ修正されていません

3.1にアップグレードしたところ、これに見舞われました。 JSON.NETに戻ります...(GUIDキーを使用します)

ドットネットコア3.1
辞書また、キー内のオブジェクトでさえ実際には文字列であり、機能していません

他の人が指摘しているように、コンパイル時にこれが表示されないので、私もこれにぶつかっただけで、それはなんと恐ろしいサイレント制限です。 私の場合、 Dictionary<int, List<string>>をシリアル化したいのですが、これは特にエキゾチックではありません。

彼らはそれを修正する必要がありますが、古いフォーマッター、バイナリフォーマッター初期のnewtsoft、辞書内の辞書、インターフェイス付きの辞書でも何度も見られます。 彼らはそれを修正する必要がありますが、トラブルを望まないのであれば、人々は本当にトラブルを求めるシリアル化契約に辞書のような複雑なオブジェクトを置くべきではありません-newtsoftは人々を台無しにしました。 これをマップするためにシリアライザーのカスタムに依存しているディクショナリカウントなどのすべてのパブリックプロパティを確認してください。

残念ながら、プロパティ名のC#にはこれの単純な型がないため、ディクショナリが強制されます。 だから私はただ悲しい..

回避策は次のとおりですが、完全な解決策ではありません。

   [JsonConverter(typeof(DictionaryConverter))]
   public Dictionary<string, object> ExtraProperties { get; set; } = new Dictionary<string, object>();
public class DictionaryConverter : JsonConverter<Dictionary<string, object>>
{
    public override Dictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var dictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(ref reader, options);

        foreach (string key in dictionary.Keys.ToList())
        {
            if (dictionary[key] is JsonElement je)
            {
                dictionary[key] = Unwrap(je);
            }
        }

        return dictionary;
    }

    public override void Write(Utf8JsonWriter writer, Dictionary<string, object> value, JsonSerializerOptions options)
        => JsonSerializer.Serialize(writer, value, options);

    private static object Unwrap(JsonElement je)
    {
        return je.ValueKind switch
        {
            JsonValueKind.String => je.ToString(),
            JsonValueKind.Number => je.TryGetInt64(out var l) ? l : je.GetDouble(),
            JsonValueKind.True => true,
            JsonValueKind.False => false,
            JsonValueKind.Array => je.EnumerateArray().Select(Unwrap).ToList(),
            JsonValueKind.Object => je.EnumerateObject().ToDictionary(x => x.Name, x => Unwrap(x.Value)),
            _ => null
        };
    }

おそらく、(いくつかの)一般的なタイプのシリアライザーのサポートをOOTBに追加できます(例:このリスト)。また、これらのタイプの1つが.IsAssignableFrom(systemDotObjectInstance.GetType())をサポートする場合はDictionary<<ins i="7">object</ins>, V>です。

これは、辞書の文字列以外のTKeyタイプにサポートを追加するための提案です。
https://github.com/dotnet/runtime/pull/32676

ご意見やご不明な点がございましたら、お気軽にお問い合わせください。

具体的には、 @ Jozkeeが辞書キーをサポートする予定の型のセットについてフィードバックを提供してください。特に、他の型をサポートする必要がある場合(基本的に、すべての組み込みプリミティブ数値型、列挙型、およびその他いくつか):
https://github.com/dotnet/runtime/blob/a5d96c0e280b56412d4614848f5ee3b1e0d7f216/src/libraries/System.Text.Json/docs/KeyConverter_spec.md#keyconverter

辞書また、キー内のオブジェクトでさえ実際には文字列であり、機能していません

@ andrew-vdb、任意のオブジェクトキーのシリアル化のサポートはサポートされないままになる可能性があります。 ただし、オブジェクトキーのランタイムタイプが「新しくサポートされた」タイプの1つである場合、機能が完了すると、そのためにシリアル化が機能します。 ただし、逆シリアル化はボックス化されたJsonElementのままになります(異なる動作をオプトインするためのこの関連する問題が解決されるまで): https

@Jozkee TValue有効になっているタイプは何ですか? おそらく、現在スタンドアロンオブジェクトとしてシリアル化できるものは何ですか?

おそらく、現在スタンドアロンオブジェクトとしてシリアル化できるものは何ですか?

はい。

デシリアライズは、ジェネリックオブジェクトタイプを通常の(プリミティブ?)タイプではなくJsonDocumentにマップするようにも見えます。

例:

string test = "[{\"id\":86,\"name\":\"test\"}]";
var SystemTextJson = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, object>>>(test);
var NewtonSoftJson = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(test);

SystemTextJson [0] ["id"]は次のように表示されます:ValueKind = Number: "86"
NewtonSoftJson [0] ["id"]は次のように表示されます:86

言及されたすべての問題の中で、これは私を最も悩ませます。 リストまたはT []または辞書Jsonが持ついくつかのタイプからCLRタイプに直接マッピングできるタイプについては、適切に逆シリアル化する必要があります。

3.0リリースは、サポートされている最も一般的なシナリオを備えた最小限の実行可能な製品となることを目的としています。

どのようにリストするのだろうか>は最も一般的なシナリオの1つではありません:

[// N objects
{"a":4},
{"b","Bla"},
]

これもSystem.Text.JsonElementに逆シリアル化されるため、double(数値)とstring(文字列)が必要になります。

私が書いている小さなプログラムでこの問題に遭遇しました。バージョンラベルの一部がjsonファイルを介して提供されています。
ラベルパーツには、ラベルパーツを挿入できるインデックスを指定するキーがあります。 これは、キーが数値であることを意味します。

{
  "parts" : {
    "1" : "alpha",
    "3" : "beta"
  }
}

Newtonsoftを使用すると、jsonを問題なくDictionary<int, string>逆シリアル化できます。 System.Text.Jsonに変換した後、シリアル化に失敗しました。

独自のDictionaryConverterDictionaryConverterを作成することで、この問題を解決しました。
また、整数を文字列から逆シリアル化できる単純なコンバーターを作成しました

これらは、シリアライザーオプションを介して登録されます: //github.com/Kieranties/SimpleVersion/blob/feature/netcore3/src/SimpleVersion.Core/Serialization/Serializer.cs#L20

これらの変更により、辞書のキーを文字列として直接読み取るのではなく、逆シリアル化することができます。 これにより、キーのサポートがさらに開かれ、独自のコンバーターをシリアル化用に登録できる任意のタイプにすることができます(たとえば、一意のキーとしてシリアル化できる列挙型/タイプ/タイプなど)。

私は正式にテストを行っていませんが、現在の開発ではこれで問題が解決したようです。

ちょっと@ Kieranties 、githubは私のために404をリンクします

ちょっと@ Kieranties 、githubは私のために404をリンクします

@AirEssY元のコメントのリンクを修正しました。現在、マスターになっていると思います: https

辞書がJavaScriptマップと同等である場合、任意の(C#で表されるJSタイプ)が受け入れられるはずです。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

A Map's keys can be any value (including functions, objects, or any primitive).

JSでマップを逆シリアル化するための標準的なアプローチの例は次のとおりです。

const countries = new Map(JSON.parse('[[1,"Bahamas (the)"],[2,"Bolivia (Plurinational State of)"]]'))

console.log(countries)

生成するもの:

Map(2) {
  1 => 'Bahamas (the)',
  2 => 'Bolivia (Plurinational State of)'
}

TL; DR:キーを文字列に制限することはJSではうまく機能しません

@Jozkeeでは、これは.NET 5でのみ提供されるのでしょうか、それとも3. *で提供されるのでしょうか?

@onionhammer .NET 5.0の場合、次のプレビュー(5.0プレビュー8)でこの機能を試すこともできます。
これを3.xに移植する予定はありません。

asp net core 3.xのソリューション:

var dic1 = new Dictionary<TKey, TValue>(); 
return Json(new { dic1 }); // does not work

var dic2 = from i in new Dictionary<TKey, TValue>() select new { i.Key, i.Value }
return Json(new { dic2 });  //works prety well

望ましい出力ではない@verloka

@Jozkeeでは、これは.NET 5でのみ提供されるのでしょうか、それとも3. *で提供されるのでしょうか?

これは3.xにバックポートされませんが、プロジェクトにSystem.Text.Json NuGetパッケージを使用して追加し、

このページは役に立ちましたか?
0 / 5 - 0 評価