using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class MemoryConverter<T> : JsonConverter<Memory<T>>
public override void WriteJson(JsonWriter writer, Memory<T> value, JsonSerializer serializer) =>
serializer.SerializeMemory((ReadOnlyMemory<T>)value, writer, false);
public override Memory<T> ReadJson(JsonReader reader, Type objectType, Memory<T> existingValue, bool hasExistingValue, JsonSerializer serializer) =>
reader.MoveToContentAndAssert().TokenType switch
JsonToken.String when typeof(T) == typeof(char) =>
((T [])(object)serializer.Deserialize<string>(reader).ToCharArray()).AsMemory(),
JsonToken.StartArray when typeof(T) == typeof(byte) =>
((T [])(object)serializer.Deserialize<List<byte>>(reader).ToArray()).AsMemory(),
serializer.Deserialize<T []>(reader).AsMemory()
public class ReadOnlyMemoryConverter<T> : JsonConverter<ReadOnlyMemory<T>>
public override void WriteJson(JsonWriter writer, ReadOnlyMemory<T> value, JsonSerializer serializer) =>
serializer.SerializeMemory((ReadOnlyMemory<T>)value, writer, serializeAsString : true);
public override ReadOnlyMemory<T> ReadJson(JsonReader reader, Type objectType, ReadOnlyMemory<T> existingValue, bool hasExistingValue, JsonSerializer serializer) =>
reader.MoveToContentAndAssert().TokenType switch
JsonToken.String when typeof(T) == typeof(char) =>
(ReadOnlyMemory<T>)(object)serializer.Deserialize<string>(reader).AsMemory(),
JsonToken.StartArray when typeof(T) == typeof(byte) =>
((T [])(object)serializer.Deserialize<List<byte>>(reader).ToArray()).AsMemory(),
serializer.Deserialize<T []>(reader).AsMemory()
public static partial class JsonExtensions
internal static void SerializeMemory<T>(this JsonSerializer serializer, ReadOnlyMemory<T> value, JsonWriter writer, bool serializeAsString)
case ReadOnlyMemory<byte> m when MemoryMarshal.TryGetArray(m, out var seg) && seg.Offset == 0 && seg.Count == seg.Array.Length:
writer.WriteValue(seg.Array);
case ReadOnlyMemory<byte> m:
writer.WriteValue(m.ToArray());
case ReadOnlyMemory<char> m when serializeAsString:
writer.WriteValue(m.ToString());
serializer.Serialize(writer, MemoryMarshal.ToEnumerable(value));
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
ArgumentNullException.ThrowIfNull(reader);
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
ArgumentNullException.ThrowIfNull(reader);
throw new JsonReaderException("Unexpected end of JSON stream.");
[JsonConverter(typeof(MemoryConverter<double>))]
public Memory<double> PROP1 { get; set; }
public static void Test()
Test("\U00029E3D\U00029E3D foo bar".ToCharArray().AsMemory());
Test("\U00029E3D\U00029E3D".ToCharArray().AsMemory().Slice(0, 3));
Test<double>(new [] { 1.1, 2.2, 3.3 }.AsMemory());
Test(Enumerable.Range(0, 100).Select(i => (byte)i).ToArray().AsMemory());
Test(Enumerable.Range(0, 100).Select(i => (byte)i).ToArray().AsMemory().Slice(3, 7) );
PROP1 = new [] { 7200.0, 7200.0, 7200.0, 10101, 202020, Math.PI }.AsMemory(),
var settings = new JsonSerializerSettings
Converters = { new MemoryConverter<double>(), new ReadOnlyMemoryConverter<double>() },
var json = JsonConvert.SerializeObject(class1, settings);
var model2 = JsonConvert.DeserializeObject<CLASS1>(json, settings);
Assert.That(MemoryMarshal.ToEnumerable((ReadOnlyMemory<double>)class1.PROP1).SequenceEqual(MemoryMarshal.ToEnumerable((ReadOnlyMemory<double>)model2.PROP1)));
public static void Test<T>(Memory<T> items0)
var settings = new JsonSerializerSettings
Converters = { new MemoryConverter<T>(), new ReadOnlyMemoryConverter<T>() },
var json0 = JsonConvert.SerializeObject(items0, settings);
Console.WriteLine(json0);
var items1 = JsonConvert.DeserializeObject<Memory<T>>(json0, settings);
Assert.That(MemoryMarshal.ToEnumerable((ReadOnlyMemory<T>)items0).SequenceEqual(MemoryMarshal.ToEnumerable((ReadOnlyMemory<T>)items1)));
ReadOnlyMemory<T> readOnlyItems0 = items0;
var json1 = JsonConvert.SerializeObject(readOnlyItems0, settings);
Console.WriteLine(json1);
var readOnlyItems1 = JsonConvert.DeserializeObject<ReadOnlyMemory<T>>(json1, settings);
Assert.That(MemoryMarshal.ToEnumerable(readOnlyItems0).SequenceEqual(MemoryMarshal.ToEnumerable(readOnlyItems1)));
if (typeof(T) == typeof(string))
var s = System.Text.Json.JsonSerializer.Deserialize<string>(json1);
Assert.AreEqual(s, readOnlyItems1.ToString());
static void TestByteList()
var bytes = Enumerable.Range(1, 21).Select(i => (byte)i).ToList();
var settings = new JsonSerializerSettings
Converters = { new MemoryConverter<byte>(), new ReadOnlyMemoryConverter<byte>() },
var json = JsonConvert.SerializeObject(bytes, settings);
var memory = JsonConvert.DeserializeObject<Memory<byte>>(json, settings);
Assert.That(bytes.SequenceEqual(MemoryMarshal.ToEnumerable((ReadOnlyMemory<byte>)memory)));
var readOnlyMemory = JsonConvert.DeserializeObject<ReadOnlyMemory<byte>>(json, settings);
Assert.That(bytes.SequenceEqual(MemoryMarshal.ToEnumerable(readOnlyMemory)));
public static void Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");