using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Microsoft.VisualStudio.Extensibility.UI;
public List<Resource> resourceList = new();
public ObservableList<Resource> observableResourceList = new();
public ReactiveProperty<int> number = new ();
public ReactiveProperty<string> word = new ();
public ReactiveProperty<string> anotherWord = new ();
public ReactiveProperty<string> nullInitially;
public ReactiveProperty<int> nullIntInitially;
public ReactiveProperty<int?> nullNullableIntInitially;
public static TestData Create() =>
resourceList = { new Resource("banana", 10), new Resource("apple", 3) },
observableResourceList = { new Resource("oBanana", 5), new Resource("oApple", 2) },
number = new ReactiveProperty<int>(9),
word = new ReactiveProperty<string>("word string"),
public readonly string Name;
public ReactiveProperty<int> Count;
public Resource(string name, int count)
Count = new ReactiveProperty<int>(count);
public class ReactivePropertyJsonConverter<T> : JsonConverter<ReactiveProperty<T>>
public override ReactiveProperty<T>? ReadJson(JsonReader reader, Type objectType, ReactiveProperty<T>? existingValue, bool hasExistingValue, JsonSerializer serializer)
var isNull = reader.MoveToContentAndAssert().TokenType == JsonToken.Null;
if (isNull && typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null)
var innerValue = !isNull ? serializer.Deserialize<T>(reader) : default(T);
if (hasExistingValue && existingValue != null)
existingValue.Value = (innerValue ?? default(T))!;
else if (objectType == typeof(ReactiveProperty<T>))
existingValue = innerValue == null ? new() : new(innerValue!);
existingValue = innerValue == null
? (ReactiveProperty<T>)Activator.CreateInstance(objectType)!
: (ReactiveProperty<T>)Activator.CreateInstance(objectType, innerValue)!;
public override void WriteJson(JsonWriter writer, ReactiveProperty<T>? value, JsonSerializer serializer) =>
serializer.Serialize(writer, value == null ? default(T?) : value.Value);
public static partial class JsonExtensions
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.");
public class ReactivePropertyJsonConverter : JsonConverter
static readonly ConcurrentDictionary<Type, JsonConverter> Converters = new();
static JsonConverter CreateReactivePropertyConverterOfT(Type valueType) =>
(JsonConverter)Activator.CreateInstance(typeof(ReactivePropertyJsonConverter<>).MakeGenericType(valueType))!;
static JsonConverter GetReactivePropertyConverterOfT(Type valueType) =>
Converters.GetOrAdd(valueType, CreateReactivePropertyConverterOfT);
static Type? GetReactivePropertyValueType(Type objectType) =>
objectType.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ReactiveProperty<>)).FirstOrDefault()?.GetGenericArguments()[0];
public override bool CanConvert(Type objectType) => GetReactivePropertyValueType(objectType) != null;
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) =>
GetReactivePropertyConverterOfT(GetReactivePropertyValueType(objectType)!).ReadJson(reader, objectType, existingValue, serializer);
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
GetReactivePropertyConverterOfT(GetReactivePropertyValueType(value.GetType())!).WriteJson(writer, value, serializer);
public static partial class JsonExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
public class DataStorageAgent
static UTF8Encoding DefaultEncoding { get; } = new UTF8Encoding(false, true);
static JsonSerializerSettings DefaultSettings { get; } = new ()
Converters = { new ReactivePropertyJsonConverter() },
Formatting = Formatting.Indented,
public T? Load<T>(string saveFilePath) where T : class
using var textReader = new StreamReader(saveFilePath, DefaultEncoding);
using var jsonReader = new JsonTextReader(textReader);
return JsonSerializer.CreateDefault(DefaultSettings).Deserialize<T>(jsonReader);
public void Save(object saveData, string saveFilePath)
using var textWriter = new StreamWriter(saveFilePath, false, DefaultEncoding);
JsonSerializer.CreateDefault(DefaultSettings).Serialize(textWriter, saveData);
public static void LogError(object s) => Console.WriteLine(s);
public static void Test()
string fileName = "Question78770520.json";
var testData = TestData.Create();
var agent = new DataStorageAgent();
agent.Save(testData, fileName);
var json = File.ReadAllText(fileName);
var test2 = agent.Load<TestData>(fileName);
agent.Save(test2!, fileName);
var json2 = File.ReadAllText(fileName);
Console.WriteLine(json2);
Assert.That(json == json2);
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("{0} version: {1}", typeof(R3.ReactiveProperty<>).Namespace, typeof(R3.ReactiveProperty<>).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");