diff --git a/docs/standard/serialization/system-text-json/immutability.md b/docs/standard/serialization/system-text-json/immutability.md index 24f2a0b00581e..6b20cd997d86c 100644 --- a/docs/standard/serialization/system-text-json/immutability.md +++ b/docs/standard/serialization/system-text-json/immutability.md @@ -30,7 +30,7 @@ The parameter names of a parameterized constructor must match the property names :::code language="csharp" source="snippets/how-to-5-0/csharp/ImmutableTypesCtorParms.cs" highlight="9,13-15"::: -Besides `[JsonPropertyName]` the following attributes support deserialization with parameterized constructors: +Besides `[JsonPropertyName]`, the following attributes support deserialization with parameterized constructors: * [[JsonConverter]](xref:System.Text.Json.Serialization.JsonConverterAttribute) * [[JsonIgnore]](xref:System.Text.Json.Serialization.JsonIgnoreAttribute) diff --git a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md index 40cf5e5b519e0..b26f848868fb6 100644 --- a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md +++ b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md @@ -68,6 +68,8 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi | `DefaultContractResolver` to ignore properties | ✔️ [DefaultJsonTypeInfoResolver class](#conditionally-ignore-a-property) | | Polymorphic serialization | ✔️ [[JsonDerivedType] attribute](#polymorphic-serialization) | | Polymorphic deserialization | ✔️ [Type discriminator on [JsonDerivedType] attribute](#polymorphic-deserialization) | +| Deserialize string enum value | ✔️ [Deserialize string enum values](#deserialize-string-enum-values) | +| `MissingMemberHandling` global setting | ✔️ [Handle missing members](#handle-missing-members) | | Support for a broad range of types | ⚠️ [Some types require custom converters](#types-without-built-in-support) | | Deserialize inferred type to `object` properties | ⚠️ [Not supported, workaround, sample](#deserialization-of-object-properties) | | Deserialize JSON `null` literal to non-nullable value types | ⚠️ [Not supported, workaround, sample](#deserialize-null-to-non-nullable-type) | @@ -76,7 +78,6 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi | `ObjectCreationHandling` global setting | ⚠️ [Not supported, workaround](#reuse-rather-than-replace-properties) | | Add to collections without setters | ⚠️ [Not supported, workaround](#add-to-collections-without-setters) | | Support for `System.Runtime.Serialization` attributes | ⚠️ [Not supported, workaround, sample](#systemruntimeserialization-attributes) | -| `MissingMemberHandling` global setting | ⚠️ [Not supported, workaround, sample](#handle-missing-members) | | `JsonObjectAttribute` | ⚠️ [Not supported, workaround](#jsonobjectattribute) | | Allow property names without quotes | ❌ [Not supported by design](#json-strings-property-names-and-string-values) | | Allow single quotes around string values | ❌ [Not supported by design](#json-strings-property-names-and-string-values) | @@ -115,6 +116,7 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi | `DefaultContractResolver` to ignore properties | ✔️ [DefaultJsonTypeInfoResolver class](#conditionally-ignore-a-property) | | Polymorphic serialization | ✔️ [[JsonDerivedType] attribute](#polymorphic-serialization) | | Polymorphic deserialization | ✔️ [Type discriminator on [JsonDerivedType] attribute](#polymorphic-deserialization) | +| Deserialize string enum value | ✔️ [Deserialize string enum values](#deserialize-string-enum-values) | | Support for a broad range of types | ⚠️ [Some types require custom converters](#types-without-built-in-support) | | Deserialize inferred type to `object` properties | ⚠️ [Not supported, workaround, sample](#deserialization-of-object-properties) | | Deserialize JSON `null` literal to non-nullable value types | ⚠️ [Not supported, workaround, sample](#deserialize-null-to-non-nullable-type) | @@ -159,6 +161,7 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi | `ReferenceLoopHandling` global setting | ✔️ [ReferenceHandling global setting](#preserve-object-references-and-handle-loops) | | Callbacks | ✔️ [Callbacks](#callbacks) | | NaN, Infinity, -Infinity | ✔️ [Supported](#nan-infinity--infinity) | +| Deserialize string enum value | ✔️ [Deserialize string enum values](#deserialize-string-enum-values) | | Support for a broad range of types | ⚠️ [Some types require custom converters](#types-without-built-in-support) | | Polymorphic serialization | ⚠️ [Not supported, workaround, sample](#polymorphic-serialization) | | Polymorphic deserialization | ⚠️ [Not supported, workaround, sample](#polymorphic-deserialization) | @@ -385,6 +388,30 @@ Custom converters can be implemented for types that don't have built-in support. To support polymorphic deserialization in older .NET versions, create a converter like the example in [How to write custom converters](converters-how-to.md#support-polymorphic-deserialization). +### Deserialize string enum values + +By default, System.Text.Json doesn't support deserializing string enum values, whereas `Newtonsoft.Json` does. For example, the following code throws a : + +```csharp +string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }"; +var _ = JsonSerializer.Deserialize(json); // Throws exception. + +class MyObj +{ + public string Text { get; set; } = ""; + public MyEnum Enum { get; set; } +} + +enum MyEnum +{ + One, + Two, + Three +} +``` + +However, you can enable deserialization of string enum values by using the converter. For more information, see [Enums as strings](customize-properties.md#enums-as-strings). + ### Deserialization of object properties When `Newtonsoft.Json` deserializes to , it: @@ -597,11 +624,13 @@ System.Text.Json doesn't have built-in support for these attributes. However, st ### Handle missing members -If the JSON that's being deserialized includes properties that are missing in the target type, `Newtonsoft.Json` can be configured to throw exceptions. ignores extra properties in the JSON, except when you use the [[JsonExtensionData] attribute](handle-overflow.md). +If the JSON that's being deserialized includes properties that are missing in the target type, `Newtonsoft.Json` can be configured to throw exceptions. By default, ignores extra properties in the JSON, except when you use the [[JsonExtensionData] attribute](handle-overflow.md). -Starting in .NET 7, you can use [contract customization](custom-contracts.md) as a workaround to match the `Newtonsoft.Json` functionality and throw an exception for JSON properties that don't exist in the target type. The following code snippet shows an example. +In .NET 8 and later versions, you can set your preference for whether to skip or disallow unmapped JSON properties using one of the following means: -:::code language="csharp" source="snippets/migrate-from-newtonsoft/MissingMemberHandling.cs"::: +* Apply the attribute to the type you're deserializing to. +* To set your preference globally, set the property. Or, for source generation, set the property and apply the attribute to your class. +* Customize the property. ### JsonObjectAttribute diff --git a/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/Migrate.csproj b/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/Migrate.csproj deleted file mode 100644 index 59ed84ca9213d..0000000000000 --- a/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/Migrate.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net7.0 - enable - enable - - - diff --git a/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/MissingMemberHandling.cs b/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/MissingMemberHandling.cs deleted file mode 100644 index 030329e26d6d5..0000000000000 --- a/docs/standard/serialization/system-text-json/snippets/migrate-from-newtonsoft/MissingMemberHandling.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Text.Json.Serialization.Metadata; -using System.Text.Json; - -var options = new JsonSerializerOptions -{ - TypeInfoResolver = new DefaultJsonTypeInfoResolver - { - Modifiers = { AddMissingMemberHandling } - } -}; - -string json = """{"Name" : "John", "Surname" : "Doe", "Age" : 99 }"""; -// Fails with: -// Unhandled exception. System.Text.Json.JsonException: JSON properties -// Surname, Age could not bind to any members of type MyPoco -JsonSerializer.Deserialize(json, options); - -static void AddMissingMemberHandling(JsonTypeInfo typeInfo) -{ - if (typeInfo.Kind == JsonTypeInfoKind.Object && - typeInfo.Properties.All(prop => !prop.IsExtensionData) && - typeInfo.OnDeserialized is null) - { - // Dynamically attach dictionaries to deserialized objects. - var cwt = new ConditionalWeakTable>(); - - JsonPropertyInfo propertyInfo = - typeInfo.CreateJsonPropertyInfo(typeof(Dictionary), "__extensionDataAttribute"); - propertyInfo.Get = obj => cwt.TryGetValue(obj, out Dictionary? value) ? value : null; - propertyInfo.Set = (obj, value) => cwt.Add(obj, (Dictionary)value!); - propertyInfo.IsExtensionData = true; - typeInfo.Properties.Add(propertyInfo); - typeInfo.OnDeserialized = obj => - { - if (cwt.TryGetValue(obj, out Dictionary? dict)) - { - cwt.Remove(obj); - throw new JsonException($"JSON properties {String.Join(", ", dict.Keys)} " + - $"could not bind to any members of type {typeInfo.Type}"); - } - }; - } -} - -public class MyPoco -{ - public string? Name { get; set; } -}