Skip to content

Commit

Permalink
ReactiveProperty JsonSerializable
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Jan 30, 2024
1 parent f26c78a commit 1820775
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 26 deletions.
52 changes: 27 additions & 25 deletions sandbox/ConsoleApp1/Program.cs
Original file line number Diff line number Diff line change
@@ -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<Unit>();
//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<int>(1000);

//});

var json = JsonSerializer.Serialize(v, options);
Console.WriteLine(json);
var v2 = JsonSerializer.Deserialize<IgnoreCaseStringReactiveProperty>(json, options);
Console.WriteLine(v2!.Value);


Observable.Create<int>(observer =>
{
observer.OnNext(1);

return Disposable.Empty;
});

Observable.Create<int>(async (observer, ct) =>
//[JsonConverter(typeof(IgnoreCaseStringReactivePropertyJsonConverter))]
public class IgnoreCaseStringReactiveProperty : ReactiveProperty<string>
{
observer.OnNext(1);
await Task.Delay(1000, ct);
});
public IgnoreCaseStringReactiveProperty(string value)
: base(value, StringComparer.OrdinalIgnoreCase)
{

Observable.CreateFrom(Gen);
}
}

static async IAsyncEnumerable<int> Gen([EnumeratorCancellation] CancellationToken ct)
internal class IgnoreCaseStringReactivePropertyJsonConverter : ReactivePropertyJsonConverter<string>
{
yield return 1;
await Task.Delay(1000, ct);
yield return 2;
await Task.Delay(1000, ct);
yield return 3;
protected override ReactiveProperty<string> CreateReactiveProperty(string value)
{
return new IgnoreCaseStringReactiveProperty(value);
}
}
4 changes: 4 additions & 0 deletions src/R3/BindableReactiveProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> : ReactiveProperty<T>, INotifyPropertyChanged, INotifyDataErrorInfo, IBindableReactiveProperty
{
// ctor
Expand Down
110 changes: 109 additions & 1 deletion src/R3/ReactiveProperty.cs
Original file line number Diff line number Diff line change
@@ -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<T> : Observable<T>, IDisposable
{
Expand All @@ -12,6 +18,10 @@ protected virtual void OnReceiveError(Exception exception) { }
// almostly same code as Subject<T>.

// allow inherit

#if NET6_0_OR_GREATER
[JsonConverter(typeof(ReactivePropertyJsonConverterFactory))]
#endif
public class ReactiveProperty<T> : ReadOnlyReactiveProperty<T>, ISubject<T>
{
T value;
Expand Down Expand Up @@ -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<T> : JsonConverter<ReactiveProperty<T>>
{
public override void Write(Utf8JsonWriter writer, ReactiveProperty<T> value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value.Value, options);
}

public override ReactiveProperty<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var v = JsonSerializer.Deserialize<T>(ref reader, options);
return CreateReactiveProperty(v!);
}

// allow customize
protected virtual ReactiveProperty<T> CreateReactiveProperty(T value)
{
return new ReactiveProperty<T>(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<T> : ReactivePropertyJsonConverter<T>
{
protected override ReactiveProperty<T> CreateReactiveProperty(T value)
{
return new BindableReactiveProperty<T>(value);
}
}


#endif

0 comments on commit 1820775

Please sign in to comment.