Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/standard/serialization/system-text-json-converters-how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ You can also write custom converters to customize or extend `System.Text.Json` w
* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties).
* [Support Dictionary with non-string key](#support-dictionary-with-non-string-key).
* [Support polymorphic deserialization](#support-polymorphic-deserialization).
* [Support round-trip for Stack\<T>](#support-round-trip-for-stackt).

## Custom converter patterns

Expand Down Expand Up @@ -172,6 +173,7 @@ The following sections provide converter samples that address some common scenar
* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties)
* [Support Dictionary with non-string key](#support-dictionary-with-non-string-key)
* [Support polymorphic deserialization](#support-polymorphic-deserialization)
* [Support round-trip for Stack\<T>](#support-round-trip-for-stackt).

### Deserialize inferred types to object properties

Expand Down Expand Up @@ -279,6 +281,26 @@ The converter can deserialize JSON that was created by using the same converter

The converter code in the preceding example reads and writes each property manually. An alternative is to call `Deserialize` or `Serialize` to do some of the work. For an example, see [this StackOverflow post](https://stackoverflow.com/a/59744873/12509023).

### Support round-trip for Stack\<T>

If you deserialize a JSON string into a <xref:System.Collections.Generic.Stack%601> object and then serialize that object, the contents of the stack are in reverse order. This behavior applies to the following types and interface, and user-defined types that derive from them:

* <xref:System.Collections.Stack>
* <xref:System.Collections.Generic.Stack%601>
* <xref:System.Collections.Concurrent.ConcurrentStack%601>
* <xref:System.Collections.Immutable.ImmutableStack%601>
* <xref:System.Collections.Immutable.IImmutableStack%601>

To support serialization and deserialization that retains the original order in the stack, a custom converter is required.

The following code shows a custom converter that enables round-tripping to and from `Stack<T>` objects:

[!code-csharp[](~/samples/snippets/core/system-text-json/csharp/JsonConverterFactoryForStackOfT.cs)]

The following code registers the converter:

[!code-csharp[](~/samples/snippets/core/system-text-json/csharp/RoundtripStackOfT.cs?name=SnippetRegister)]

## Other custom converter samples

The [Migrate from Newtonsoft.Json to System.Text.Json](system-text-json-migrate-from-newtonsoft-how-to.md) article contains additional samples of custom converters.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class JsonConverterFactoryForStackOfT : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.IsGenericType &&
typeToConvert.GetGenericTypeDefinition() == typeof(Stack<>);
}

public override JsonConverter CreateConverter(
Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert.IsGenericType &&
typeToConvert.GetGenericTypeDefinition() == typeof(Stack<>));

Type elementType = typeToConvert.GetGenericArguments()[0];

JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(JsonConverterForStackOfT<>)
.MakeGenericType(new Type[] { elementType }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null)!;

return converter;
}
}

public class JsonConverterForStackOfT<T> : JsonConverter<Stack<T>>
{
public override Stack<T> Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray || !reader.Read())
{
throw new JsonException();
}

var elements = new Stack<T>();

while (reader.TokenType != JsonTokenType.EndArray)
{
elements.Push(JsonSerializer.Deserialize<T>(ref reader, options));

if (!reader.Read())
{
throw new JsonException();
}
}

return elements;
}

public override void Write(
Utf8JsonWriter writer, Stack<T> value, JsonSerializerOptions options)
{
writer.WriteStartArray();

var reversed = new Stack<T>(value);

foreach (T item in reversed)
{
JsonSerializer.Serialize(writer, item, options);
}

writer.WriteEndArray();
}
}
}
3 changes: 3 additions & 0 deletions samples/snippets/core/system-text-json/csharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ static async Task Main(string[] args)
Console.WriteLine("\n============================= Roundtrip enum as string\n");
RoundtripEnumAsString.Run();

Console.WriteLine("\n============================= Roundtrip Stack<T>\n");
RoundtripStackOfT.Run();

Console.WriteLine("\n============================= Serialize polymorphic\n");
SerializePolymorphic.Run();

Expand Down
30 changes: 30 additions & 0 deletions samples/snippets/core/system-text-json/csharp/RoundtripStackOfT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json;

namespace SystemTextJsonSamples
{
public class RoundtripStackOfT
{
public static void Run()
{
Console.WriteLine("Deserialize JSON string [1, 2, 3], then serialize it back to JSON.");
Stack<int> stack = JsonSerializer.Deserialize<Stack<int>>("[1, 2, 3]");
string serialized = JsonSerializer.Serialize(stack);
Console.WriteLine($"Result is in reverse order: {serialized}");

Console.WriteLine("Deserialize JSON string [1, 2, 3] with custom converter, then serialize it back to JSON.");
// <SnippetRegister>
var options = new JsonSerializerOptions
{
Converters = { new JsonConverterFactoryForStackOfT() },
};
// </SnippetRegister>
stack = JsonSerializer.Deserialize<Stack<int>>("[1, 2, 3]", options);
serialized = JsonSerializer.Serialize(stack, options);
Console.WriteLine($"Result is in same order: {serialized}");
}
}
}