Skip to content

Commit

Permalink
Added experimental fix for interface serialization
Browse files Browse the repository at this point in the history
The communicator was failing to (de)serialize the communicator wrapper messages because it was unable to determine the type. I added a special converter for interfaces that includes the type name, so the serializer knows how to handle the JSON data.
  • Loading branch information
exectails committed Jul 7, 2024
1 parent 8a80c3e commit 6a51735
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 1 deletion.
78 changes: 78 additions & 0 deletions Yggdrasil/Network/Communication/InterfaceConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Yggdrasil.Network.Communication
{
/// <summary>
/// A converter for interfaces.
/// </summary>
/// <typeparam name="T"></typeparam>
public class InterfaceConverter<T> : JsonConverter<T> where T : class
{
private const string TypePropertyName = "Type";
private const string DataPropertyName = "Data";

/// <summary>
/// Writes the value to the JSON writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="options"></param>
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString(TypePropertyName, value.GetType().AssemblyQualifiedName);
writer.WritePropertyName(DataPropertyName);
JsonSerializer.Serialize(writer, value, value.GetType(), options);
writer.WriteEndObject();
}

/// <summary>
/// Reads a value from the JSON reader.
/// </summary>
/// <param name="reader"></param>
/// <param name="typeToConvert"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="JsonException"></exception>
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException();

reader.Read(); // Object start

if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != TypePropertyName)
throw new JsonException();

reader.Read(); // Property name

var typeName = reader.GetString();
var type = Type.GetType(typeName) ?? throw new JsonException($"Unable to find type: {typeName}");

reader.Read(); // Property name

if (reader.GetString() != DataPropertyName)
throw new JsonException();

reader.Read(); // Object start

var result = JsonSerializer.Deserialize(ref reader, type, options) as T;

reader.Read(); // Object end

return result;
}

/// <summary>
/// Determines whether the specified type can be converted.
/// </summary>
/// <param name="typeToConvert"></param>
/// <returns></returns>
public override bool CanConvert(Type typeToConvert)
{
return typeof(T).IsAssignableFrom(typeToConvert);
}
}
}
2 changes: 2 additions & 0 deletions Yggdrasil/Network/Communication/Messages/BroadcastMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;

namespace Yggdrasil.Network.Communication.Messages
{
Expand All @@ -16,6 +17,7 @@ public class BroadcastMessage : ICommMessage
/// <summary>
/// Returns the message to be broadcasted.
/// </summary>
[JsonConverter(typeof(InterfaceConverter<ICommMessage>))]
public ICommMessage Message { get; }

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions Yggdrasil/Network/Communication/Messages/RequestMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;
using System.Threading;

namespace Yggdrasil.Network.Communication.Messages
Expand All @@ -19,6 +20,7 @@ public class RequestMessage : ICommMessage
/// <summary>
/// Returns the message that a response is requested for.
/// </summary>
[JsonConverter(typeof(InterfaceConverter<ICommMessage>))]
public ICommMessage Message { get; }

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions Yggdrasil/Network/Communication/Messages/ResponseMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;

namespace Yggdrasil.Network.Communication.Messages
{
Expand All @@ -16,6 +17,7 @@ public class ResponseMessage : ICommMessage
/// <summary>
/// Returns the actual response message.
/// </summary>
[JsonConverter(typeof(InterfaceConverter<ICommMessage>))]
public ICommMessage Message { get; }

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Yggdrasil/Yggdrasil.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>$(AssemblyName)</Title>
<Version>1.0.3</Version>
<Version>1.0.4</Version>
<Authors>exec</Authors>
<Description>Game server and emulation library.</Description>
<RepositoryUrl>https://github.com/exectails/Yggdrasil</RepositoryUrl>
Expand Down

0 comments on commit 6a51735

Please sign in to comment.