diff --git a/sandbox/ConsoleApp1/Program.cs b/sandbox/ConsoleApp1/Program.cs index 784196b6..d85d85f5 100644 --- a/sandbox/ConsoleApp1/Program.cs +++ b/sandbox/ConsoleApp1/Program.cs @@ -1,48 +1,50 @@ using R3; using System.ComponentModel.DataAnnotations; using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; Console.WriteLine("hello"); -// System.Linq.AsyncEnumerable.Range(1,10) -//Dump.Factory(); -//System.Reactive.Linq.Observable.Range(1,10).SelectMany( - -//var onClick = new Subject(); -//var httpClient = new HttpClient(); +//JsonSerializerOptions.Default.TypeInfoResolver +// JsonSerializerOptions.Default.Converters.Add(new IgnoreCaseStringReactivePropertyJsonConverter()); +var options = new JsonSerializerOptions +{ + Converters = { new IgnoreCaseStringReactivePropertyJsonConverter() }, +}; -//onClick.SelectAwait(async x => -//{ +var v = new IgnoreCaseStringReactiveProperty("aaa"); +// var v = new ReactiveProperty(1000); -//}); +var json = JsonSerializer.Serialize(v, options); +Console.WriteLine(json); +var v2 = JsonSerializer.Deserialize(json, options); +Console.WriteLine(v2!.Value); -Observable.Create(observer => -{ - observer.OnNext(1); - return Disposable.Empty; -}); -Observable.Create(async (observer, ct) => +//[JsonConverter(typeof(IgnoreCaseStringReactivePropertyJsonConverter))] +public class IgnoreCaseStringReactiveProperty : ReactiveProperty { - observer.OnNext(1); - await Task.Delay(1000, ct); -}); + public IgnoreCaseStringReactiveProperty(string value) + : base(value, StringComparer.OrdinalIgnoreCase) + { -Observable.CreateFrom(Gen); + } +} -static async IAsyncEnumerable Gen([EnumeratorCancellation] CancellationToken ct) +internal class IgnoreCaseStringReactivePropertyJsonConverter : ReactivePropertyJsonConverter { - yield return 1; - await Task.Delay(1000, ct); - yield return 2; - await Task.Delay(1000, ct); - yield return 3; + protected override ReactiveProperty CreateReactiveProperty(string value) + { + return new IgnoreCaseStringReactiveProperty(value); + } } diff --git a/src/R3/BindableReactiveProperty.cs b/src/R3/BindableReactiveProperty.cs index e877f56a..1bee9737 100644 --- a/src/R3/BindableReactiveProperty.cs +++ b/src/R3/BindableReactiveProperty.cs @@ -15,6 +15,10 @@ public interface IBindableReactiveProperty } // all operators need to call from UI Thread(not thread-safe) + +#if NET6_0_OR_GREATER +[System.Text.Json.Serialization.JsonConverter(typeof(BindableReactivePropertyJsonConverterFactory))] +#endif public class BindableReactiveProperty : ReactiveProperty, INotifyPropertyChanged, INotifyDataErrorInfo, IBindableReactiveProperty { // ctor diff --git a/src/R3/ReactiveProperty.cs b/src/R3/ReactiveProperty.cs index a6859552..36ae56ac 100644 --- a/src/R3/ReactiveProperty.cs +++ b/src/R3/ReactiveProperty.cs @@ -1,4 +1,10 @@ -namespace R3; +#if NET6_0_OR_GREATER +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +#endif + +namespace R3; public abstract class ReadOnlyReactiveProperty : Observable, IDisposable { @@ -12,6 +18,10 @@ protected virtual void OnReceiveError(Exception exception) { } // almostly same code as Subject. // allow inherit + +#if NET6_0_OR_GREATER +[JsonConverter(typeof(ReactivePropertyJsonConverterFactory))] +#endif public class ReactiveProperty : ReadOnlyReactiveProperty, ISubject { T value; @@ -182,3 +192,101 @@ public void Dispose() } } } + +#if NET6_0_OR_GREATER + +public class ReactivePropertyJsonConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return GetReactivePropertyType(typeToConvert) != null; + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var t = GetReactivePropertyType(typeToConvert); + if (t != null) + { + var rt = GenericConverterType.MakeGenericType(t.GetGenericArguments()[0]); + return (JsonConverter?)Activator.CreateInstance(rt, false); + } + else + { + return null; + } + } + + Type? GetReactivePropertyType(Type? type) + { + while (type != null) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ReactiveProperty<>)) + { + return type; + } + + type = type.BaseType; + } + return null; + } + + // allow customize + protected virtual Type GenericConverterType => typeof(ReactivePropertyJsonConverter<>); +} + +public class ReactivePropertyJsonConverter : JsonConverter> +{ + public override void Write(Utf8JsonWriter writer, ReactiveProperty value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + public override ReactiveProperty? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var v = JsonSerializer.Deserialize(ref reader, options); + return CreateReactiveProperty(v!); + } + + // allow customize + protected virtual ReactiveProperty CreateReactiveProperty(T value) + { + return new ReactiveProperty(value); + } + + public override bool CanConvert(Type typeToConvert) + { + return GetReactivePropertyType(typeToConvert) != null; + } + + Type? GetReactivePropertyType(Type? type) + { + while (type != null) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ReactiveProperty<>)) + { + return type; + } + + type = type.BaseType; + } + return null; + } +} + +// for BindableReactiveProperty + +internal class BindableReactivePropertyJsonConverterFactory : ReactivePropertyJsonConverterFactory +{ + protected override Type GenericConverterType => typeof(BindableReactivePropertyJsonConverter<>); +} + +internal class BindableReactivePropertyJsonConverter : ReactivePropertyJsonConverter +{ + protected override ReactiveProperty CreateReactiveProperty(T value) + { + return new BindableReactiveProperty(value); + } +} + + +#endif