Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Knihovna System.Text.Json vytvoří kontrakt JSON pro každý typ .NET, který definuje, jak má být typ serializován a deserializován. Kontrakt je odvozen od tvaru typu, který zahrnuje charakteristiky, jako jsou jeho vlastnosti a pole, a zda implementuje rozhraní IEnumerable nebo IDictionary. Typy se mapují na kontrakty za běhu pomocí reflexe nebo v době kompilace pomocí zdrojového generátoru.
Od verze .NET 7 můžete tyto kontrakty JSON přizpůsobit, abyste měli větší kontrolu nad tím, jak se typy převedou na JSON a naopak. Následující seznam ukazuje jen některé příklady typů přizpůsobení, které můžete provést pro serializaci a deserializaci:
- Serializace privátních polí a vlastností
- Podpora více názvů pro jednu vlastnost (například pokud předchozí verze knihovny použila jiný název).
- Ignorujte vlastnosti s určitým názvem, typem nebo hodnotou.
- Rozlišovat mezi explicitními
nullhodnotami a nepřítomností hodnoty v datové části JSON. - Podporujte atributy System.Runtime.Serialization, například DataContractAttribute. Další informace naleznete v tématu atributy System.Runtime.Serialization.
- Vyvolá výjimku, pokud JSON obsahuje vlastnost, která není součástí cílového typu. Další informace naleznete v tématu Zpracování chybějících členů.
Jak se přihlásit
Existují dva způsoby, jak se zapojit do přizpůsobení. Oba zahrnují získání resolveru, jehož úlohou je poskytnout JsonTypeInfo instanci pro každý typ, který musí být serializován.
Voláním konstruktoru DefaultJsonTypeInfoResolver() získáte JsonSerializerOptions.TypeInfoResolver a poté přidáte své vlastní akce do jeho vlastnosti Modifiers.
Příklad:
JsonSerializerOptions options = new() { TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { MyCustomModifier1, MyCustomModifier2 } } };Pokud přidáte více modifikátorů, budou volány postupně.
Vytvořením vlastního resolveru, který implementuje IJsonTypeInfoResolver.
- Pokud není typ zpracován, IJsonTypeInfoResolver.GetTypeInfo by se měl
nullvrátit pro tento typ. - Můžete také kombinovat vlastní resolver s ostatními, například s výchozím resolverem. Resolveři budou dotazováni v pořadí, dokud se pro typ nevrátí hodnota, která není null JsonTypeInfo.
- Pokud není typ zpracován, IJsonTypeInfoResolver.GetTypeInfo by se měl
Konfigurovatelné aspekty
Vlastnost JsonTypeInfo.Kind určuje, jak převaděč serializuje daný typ , například jako objekt nebo jako pole a zda jsou jeho vlastnosti serializovány. Pomocí dotazu na tuto vlastnost můžete určit, které aspekty kontraktu JSON typu můžete nakonfigurovat. Existují čtyři různé druhy:
JsonTypeInfo.Kind |
Popis |
|---|---|
| JsonTypeInfoKind.Object | Převaděč serializuje typ do objektu JSON a použije jeho vlastnosti. Tento druh se používá pro většinu typů tříd a struktur a umožňuje největší flexibilitu. |
| JsonTypeInfoKind.Enumerable | Převaděč serializuje typ do pole JSON. Tento druh se používá pro typy jako List<T> a pole. |
| JsonTypeInfoKind.Dictionary | Převaděč serializuje typ do objektu JSON. Tento druh se používá pro typy, jako je Dictionary<K, V>. |
| JsonTypeInfoKind.None | Převaděč neurčuje, jak bude serializovat typ nebo jaké JsonTypeInfo vlastnosti bude používat. Tento typ se používá pro typy jako System.Object, int a string a pro všechny typy, které používají vlastní konvertor. |
Modifikátory
Modifikátor je Action<JsonTypeInfo> nebo metoda s parametrem JsonTypeInfo , který získá aktuální stav kontraktu jako argument a provede úpravy kontraktu. Můžete například iterovat přes předvyplněné vlastnosti ve zadaném JsonTypeInfo objektu, abyste našli tu, která vás zajímá, a pak upravit její vlastnost JsonPropertyInfo.Get (pro serializaci) nebo vlastnost JsonPropertyInfo.Set (pro deserializaci). Nebo můžete vytvořit novou vlastnost pomocí JsonTypeInfo.CreateJsonPropertyInfo(Type, String) a přidat ji do JsonTypeInfo.Properties kolekce.
Následující tabulka ukazuje úpravy, které můžete provést a jak je dosáhnout.
| Modifikace | Aplikovatelný JsonTypeInfo.Kind |
Jak toho dosáhnout | Příklad |
|---|---|---|---|
| Přizpůsobte hodnotu vlastnosti | JsonTypeInfoKind.Object |
Upravte delegáta JsonPropertyInfo.Get (pro serializaci) nebo delegáta JsonPropertyInfo.Set (pro deserializaci) pro vlastnost. | Zvýšení hodnoty vlastnosti |
| Přidání nebo odebrání vlastností | JsonTypeInfoKind.Object |
Přidejte nebo odeberte položky ze JsonTypeInfo.Properties seznamu. | Serializace privátních polí |
| Podmíněně serializovat vlastnost | JsonTypeInfoKind.Object |
JsonPropertyInfo.ShouldSerialize Upravte predikát vlastnosti. | Ignorování vlastností s konkrétním typem |
| Přizpůsobení zpracování čísel pro určitý typ | JsonTypeInfoKind.None |
JsonTypeInfo.NumberHandling Upravte hodnotu pro typ. | Povolit hodnotám typu int být řetězci |
Příklad: Zvýšení hodnoty vlastnosti
Podívejte se na následující příklad, kde modifikátor zvýší hodnotu určité vlastnosti deserializace úpravou jeho JsonPropertyInfo.Set delegáta. Kromě definování modifikátoru představuje příklad také nový atribut, který používá k vyhledání vlastnosti, jejíž hodnota by měla být zvýšení. Toto je příklad přizpůsobení vlastnosti.
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
namespace Serialization
{
// Custom attribute to annotate the property
// we want to be incremented.
[AttributeUsage(AttributeTargets.Property)]
class SerializationCountAttribute : Attribute
{
}
// Example type to serialize and deserialize.
class Product
{
public string Name { get; set; } = "";
[SerializationCount]
public int RoundTrips { get; set; }
}
public class SerializationCountExample
{
// Custom modifier that increments the value
// of a specific property on deserialization.
static void IncrementCounterModifier(JsonTypeInfo typeInfo)
{
foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties)
{
if (propertyInfo.PropertyType != typeof(int))
continue;
object[] serializationCountAttributes = propertyInfo.AttributeProvider?.GetCustomAttributes(typeof(SerializationCountAttribute), true) ?? Array.Empty<object>();
SerializationCountAttribute? attribute = serializationCountAttributes.Length == 1 ? (SerializationCountAttribute)serializationCountAttributes[0] : null;
if (attribute != null)
{
Action<object, object?>? setProperty = propertyInfo.Set;
if (setProperty is not null)
{
propertyInfo.Set = (obj, value) =>
{
if (value != null)
{
// Increment the value by 1.
value = (int)value + 1;
}
setProperty (obj, value);
};
}
}
}
}
public static void RunIt()
{
var product = new Product
{
Name = "Aquafresh"
};
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { IncrementCounterModifier }
}
};
// First serialization and deserialization.
string serialized = JsonSerializer.Serialize(product, options);
Console.WriteLine(serialized);
// {"Name":"Aquafresh","RoundTrips":0}
Product deserialized = JsonSerializer.Deserialize<Product>(serialized, options)!;
Console.WriteLine($"{deserialized.RoundTrips}");
// 1
// Second serialization and deserialization.
serialized = JsonSerializer.Serialize(deserialized, options);
Console.WriteLine(serialized);
// { "Name":"Aquafresh","RoundTrips":1}
deserialized = JsonSerializer.Deserialize<Product>(serialized, options)!;
Console.WriteLine($"{deserialized.RoundTrips}");
// 2
}
}
}
Všimněte si ve výstupu, že hodnota RoundTrips se při každé Product deserializaci instance zvýší.
Příklad: Serializace privátních polí
Ve výchozím nastavení System.Text.Json ignoruje privátní pole a vlastnosti. Tento příklad přidá nový atribut pro celou třídu, JsonIncludePrivateFieldsAttributekterý změní tuto výchozí hodnotu. Pokud modifikátor najde atribut u typu, přidá do něj všechna soukromá pole typu jako nové vlastnosti JsonTypeInfo.
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Serialization
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class JsonIncludePrivateFieldsAttribute : Attribute { }
[JsonIncludePrivateFields]
public class Human
{
private string _name;
private int _age;
public Human()
{
// This constructor should be used only by deserializers.
_name = null!;
_age = 0;
}
public static Human Create(string name, int age)
{
Human h = new()
{
_name = name,
_age = age
};
return h;
}
[JsonIgnore]
public string Name
{
get => _name;
set => throw new NotSupportedException();
}
[JsonIgnore]
public int Age
{
get => _age;
set => throw new NotSupportedException();
}
}
public class PrivateFieldsExample
{
static void AddPrivateFieldsModifier(JsonTypeInfo jsonTypeInfo)
{
if (jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
return;
if (!jsonTypeInfo.Type.IsDefined(typeof(JsonIncludePrivateFieldsAttribute), inherit: false))
return;
foreach (FieldInfo field in jsonTypeInfo.Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
{
JsonPropertyInfo jsonPropertyInfo = jsonTypeInfo.CreateJsonPropertyInfo(field.FieldType, field.Name);
jsonPropertyInfo.Get = field.GetValue;
jsonPropertyInfo.Set = field.SetValue;
jsonTypeInfo.Properties.Add(jsonPropertyInfo);
}
}
public static void RunIt()
{
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { AddPrivateFieldsModifier }
}
};
var human = Human.Create("Julius", 37);
string json = JsonSerializer.Serialize(human, options);
Console.WriteLine(json);
// {"_name":"Julius","_age":37}
Human deserializedHuman = JsonSerializer.Deserialize<Human>(json, options)!;
Console.WriteLine($"[Name={deserializedHuman.Name}; Age={deserializedHuman.Age}]");
// [Name=Julius; Age=37]
}
}
}
Návod
Pokud názvy privátních polí začínají podtržítky, zvažte odebrání podtržítka z názvů, když přidáte pole jako nové vlastnosti JSON.
Příklad: Ignorování vlastností s konkrétním typem
Váš model může mít vlastnosti s konkrétními názvy nebo typy, které nechcete zpřístupnit uživatelům. Můžete mít například vlastnost, která ukládá přihlašovací údaje nebo některé informace, které jsou v datové části zbytečné.
Následující příklad ukazuje, jak filtrovat vlastnosti s konkrétním typem, SecretHolder. Provede to pomocí IList<T> rozšiřující metody k odebrání všech vlastností, které mají zadaný typ ze JsonTypeInfo.Properties seznamu. Filtrované vlastnosti zcela zmizí z kontraktu, což znamená, že se na ně System.Text.Json nedívá ani během serializace, ani během deserializace.
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
namespace Serialization
{
class ExampleClass
{
public string Name { get; set; } = "";
public SecretHolder? Secret { get; set; }
}
class SecretHolder
{
public string Value { get; set; } = "";
}
class IgnorePropertiesWithType
{
private readonly Type[] _ignoredTypes;
public IgnorePropertiesWithType(params Type[] ignoredTypes)
=> _ignoredTypes = ignoredTypes;
public void ModifyTypeInfo(JsonTypeInfo ti)
{
if (ti.Kind != JsonTypeInfoKind.Object)
return;
ti.Properties.RemoveAll(prop => _ignoredTypes.Contains(prop.PropertyType));
}
}
public class IgnoreTypeExample
{
public static void RunIt()
{
var modifier = new IgnorePropertiesWithType(typeof(SecretHolder));
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { modifier.ModifyTypeInfo }
}
};
ExampleClass obj = new()
{
Name = "Password",
Secret = new SecretHolder { Value = "MySecret" }
};
string output = JsonSerializer.Serialize(obj, options);
Console.WriteLine(output);
// {"Name":"Password"}
}
}
public static class ListHelpers
{
// IList<T> implementation of List<T>.RemoveAll method.
public static void RemoveAll<T>(this IList<T> list, Predicate<T> predicate)
{
for (int i = 0; i < list.Count; i++)
{
if (predicate(list[i]))
{
list.RemoveAt(i--);
}
}
}
}
}
Příklad: Povolit, aby hodnoty typu int mohly být řetězci
Váš vstupní JSON může například obsahovat uvozovky kolem jednoho z číselných typů, ale ne u ostatních. Pokud byste měli kontrolu nad třídou, mohli byste JsonNumberHandlingAttribute použít na typ k vyřešení tohoto problému, ale nemáte. Před .NET 7 byste museli napsat vlastní převaděč pro opravu tohoto chování, který vyžaduje zápis poměrně malého kódu. Pomocí přizpůsobení kontraktu můžete přizpůsobit chování zpracování čísel pro libovolný typ.
Následující příklad změní chování pro všechny int hodnoty. Příklad lze snadno upravit tak, aby platil pro jakýkoli typ nebo pro konkrétní vlastnost libovolného typu.
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Serialization
{
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
public class AllowIntsAsStringsExample
{
static void SetNumberHandlingModifier(JsonTypeInfo jsonTypeInfo)
{
if (jsonTypeInfo.Type == typeof(int))
{
jsonTypeInfo.NumberHandling = JsonNumberHandling.AllowReadingFromString;
}
}
public static void RunIt()
{
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { SetNumberHandlingModifier }
}
};
// Triple-quote syntax is a C# 11 feature.
Point point = JsonSerializer.Deserialize<Point>("""{"X":"12","Y":"3"}""", options)!;
Console.WriteLine($"({point.X},{point.Y})");
// (12,3)
}
}
}
Bez modifikátoru, který by umožňoval čtení hodnot int z řetězce, by program skončil výjimkou.
Neošetřená výjimka. System.Text.Json.JsonException: Hodnotu JSON nelze převést na System.Int32. Cesta: $. X | Číslo řádku: 0 | Pozice bajtu v řádku: 9.
Další způsoby přizpůsobení serializace
Kromě přizpůsobení kontraktu existují další způsoby, jak ovlivnit serializace a deserializační chování, včetně následujících:
- Pomocí atributů odvozených z JsonAttribute, například JsonIgnoreAttribute a JsonPropertyOrderAttribute.
- Úpravou JsonSerializerOptions lze například nastavit zásady pojmenování nebo serializovat hodnoty výčtu jako řetězce místo čísel.
- Napsáním vlastního převaděče, který provede skutečnou práci zápisu JSON a při deserializaci vytvoří objekt.
Přizpůsobení smlouvy je vylepšením těchto předem existujících přizpůsobení, protože možná nemáte přístup k typu pro přidání atributů. Kromě toho je zápis vlastního převaděče složitý a snižuje výkon.