Improve serialization of GrainReference when using JSON (#3073)

This commit is contained in:
Reuben Bond 2017-06-01 16:41:14 -07:00 коммит произвёл Julian Dominguez
Родитель d97e91adbd
Коммит 6dafe25a5c
2 изменённых файлов: 66 добавлений и 31 удалений

Просмотреть файл

@ -287,49 +287,61 @@ namespace Orleans.Serialization
internal class GrainReferenceConverter : JsonConverter
{
private static readonly Type GrainReferenceType;
private static readonly ConcurrentDictionary<Type, GrainFactory.GrainReferenceCaster> Casters =
new ConcurrentDictionary<Type, GrainFactory.GrainReferenceCaster>();
private static readonly Func<Type, GrainFactory.GrainReferenceCaster> CreateCasterDelegate = CreateCaster;
private static readonly Type AddressableType = typeof(IAddressable);
private readonly IGrainFactory grainFactory;
private readonly JsonSerializer internalSerializer;
public GrainReferenceConverter(IGrainFactory grainFactory)
{
this.grainFactory = grainFactory;
}
static GrainReferenceConverter()
{
GrainReferenceType = typeof(GrainReference);
// Create a serializer for internal serialization which does not have a specified GrainReference serializer.
// This internal serializer will use GrainReference's ISerializable implementation for serialization and deserialization.
this.internalSerializer = JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
PreserveReferencesHandling = PreserveReferencesHandling.None,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DefaultValueHandling = DefaultValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
Formatting = Formatting.None,
Converters =
{
new IPAddressConverter(),
new IPEndPointConverter(),
new GrainIdConverter(),
new SiloAddressConverter(),
new UniqueKeyConverter()
}
});
}
public override bool CanConvert(Type objectType)
{
return GrainReferenceType.IsAssignableFrom(objectType);
return AddressableType.IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var key = (value as GrainReference)?.ToKeyString();
serializer.Serialize(writer, key);
// Serialize the grain reference using the internal serializer.
this.internalSerializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var key = serializer.Deserialize<string>(reader);
if (string.IsNullOrWhiteSpace(key)) return null;
// Deserialize using the internal serializer which will use the concrete GrainReference implementation's
// ISerializable constructor.
var result = this.internalSerializer.Deserialize(reader, objectType);
var grainRef = result as IAddressable;
if (grainRef == null) return result;
var result = GrainReference.FromKeyString(key, null);
this.grainFactory.BindGrainReference(result);
return Casters.GetOrAdd(objectType, CreateCasterDelegate)(result);
}
private static GrainFactory.GrainReferenceCaster CreateCaster(Type grainReferenceType)
{
var interfaceType = grainReferenceType.GetTypeInfo().GetCustomAttribute<GrainReferenceAttribute>().TargetType;
return GrainCasterFactory.CreateGrainReferenceCaster(interfaceType, grainReferenceType);
// Bind the deserialized grain reference to the runtime.
this.grainFactory.BindGrainReference(grainRef);
return grainRef;
}
}
#endregion
}
}

Просмотреть файл

@ -3,6 +3,7 @@ using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Orleans;
using Orleans.Runtime;
using Orleans.Serialization;
@ -101,6 +102,33 @@ namespace DefaultCluster.Tests.General
TestGrainReferenceSerialization(id, true, true);
}
[Fact, TestCategory("Serialization"), TestCategory("JSON")]
public async Task GrainReference_Json_Serialization_Nested()
{
var settings = OrleansJsonSerializer.GetDefaultSerializerSettings(HostedCluster.SerializationManager, HostedCluster.GrainFactory);
var grain = HostedCluster.GrainFactory.GetGrain<ISimpleGrain>(GetRandomGrainId());
await grain.SetA(56820);
var input = new GenericGrainReferenceHolder
{
Reference = grain as GrainReference
};
var json = JsonConvert.SerializeObject(input, settings);
var output = JsonConvert.DeserializeObject<GenericGrainReferenceHolder>(json, settings);
Assert.Equal(input.Reference, output.Reference);
var reference = output.Reference;
Assert.Equal(56820, await ((ISimpleGrain)reference).GetA());
}
[Serializable]
public class GenericGrainReferenceHolder
{
[JsonProperty]
public GrainReference Reference { get; set; }
}
[Fact, TestCategory("Serialization"), TestCategory("JSON")]
public void GrainReference_Json_Serialization_Unresolved()
{
@ -219,13 +247,8 @@ namespace DefaultCluster.Tests.General
{
var settings = OrleansJsonSerializer.GetDefaultSerializerSettings(this.HostedCluster.SerializationManager, this.GrainFactory);
// http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_JsonConvert.htm
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj, settings);
object other = Newtonsoft.Json.JsonConvert.DeserializeObject(json, obj.GetType(), settings);
#if NETSTANDARD_TODO
// On .NET Standard, currently we need to manually fixup grain references.
var otherAsRef = other as Orleans.Runtime.GrainReference;
if (otherAsRef != null) this.GrainFactory.BindGrainReference(otherAsRef);
#endif
string json = JsonConvert.SerializeObject(obj, settings);
object other = JsonConvert.DeserializeObject(json, typeof(T), settings);
return (T)other;
}
}