Skip to content

Commit

Permalink
feat: add json-based message serialization and deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Marco Bacis committed Jun 23, 2023
1 parent 354e812 commit 33558e4
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 6 deletions.
91 changes: 85 additions & 6 deletions WeArtMessageCustomSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
* https://www.weart.it/
*/

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using WeArt.Core;
using WeArt.Utils;

namespace WeArt.Messages
{
Expand All @@ -29,19 +31,38 @@ internal class WeArtMiddlewareMessageIDAttribute : Attribute
public WeArtMiddlewareMessageIDAttribute(string id) => ID = id;
}

internal class JsonMessageTemplate
{
[JsonProperty(PropertyName = "ts")]
[JsonConverter(typeof(MillisecondsEpochConverter))]
public DateTime Timestamp { get; set; }

[JsonProperty(PropertyName = "type")]
public string Type { get; set; }

[JsonProperty(PropertyName = "data")]
public JToken Data { get; set; }
}

public override bool Serialize(IWeArtMessage message, out string data)
{
try
{
if(message is IWeArtMessageCustomSerialization serializableMessage)
if (message is IWeArtMessageCustomSerialization serializableMessage)
{
data = string.Join(separator.ToString(), serializableMessage.Serialize());
return true;
}

var messageType = message.GetType();
var reflectionData = ReflectionData.GetFrom(messageType);

if (message is WeArtJsonMessage jsonMessage)
{
data = SerializeJsonMessage(reflectionData, jsonMessage);
return true;
}

var values = reflectionData.Fields
.Select(field => field.GetValue(message));

Expand All @@ -60,6 +81,26 @@ public override bool Serialize(IWeArtMessage message, out string data)
}
}

private string SerializeJsonMessage(ReflectionData reflectionData, WeArtJsonMessage jsonMessage)
{
JsonMessageTemplate json = new JsonMessageTemplate
{
Type = reflectionData.Id,
Timestamp = jsonMessage.Timestamp,
Data = JToken.FromObject(jsonMessage),
};
DefaultContractResolver jsonContract = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
JsonSerializerSettings jsonSettings = new JsonSerializerSettings
{
ContractResolver = jsonContract,
Formatting = Formatting.None,
};
return JsonConvert.SerializeObject(json, jsonSettings);
}

public override bool Deserialize(string data, out IWeArtMessage message)
{
try
Expand All @@ -73,7 +114,7 @@ public override bool Deserialize(string data, out IWeArtMessage message)

if (message is IWeArtMessageCustomSerialization deserializableMessage)
return deserializableMessage.Deserialize(split);

for (int i = 1; i < split.Length; i++)
{
var field = reflectionData.Fields[i - 1];
Expand All @@ -82,13 +123,33 @@ public override bool Deserialize(string data, out IWeArtMessage message)
}
return true;
}
catch (MessageTypeNotFound)
{
// Try with json
message = DeserializeJsonMessage(data);
return message != null;
}
catch (Exception)
{
message = null;
return false;
}
}

private IWeArtMessage DeserializeJsonMessage(string data)
{
try
{
JsonMessageTemplate json = JsonConvert.DeserializeObject<JsonMessageTemplate>(data);
ReflectionData reflectionData = ReflectionData.GetFrom(json.Type);
return (IWeArtMessage)json.Data.ToObject(reflectionData.Type) ?? null;
}
catch (MessageTypeNotFound)
{
return null;
}
}

private static string Serialize(object data)
{
if (data == null)
Expand Down Expand Up @@ -122,6 +183,8 @@ private static object Deserialize(string data, Type type)
return data;
}

internal class MessageTypeNotFound : Exception { }


private struct ReflectionData
{
Expand Down Expand Up @@ -159,7 +222,7 @@ public static ReflectionData GetFrom(string messageID)
var messageType = typeof(WeArtMessageCustomSerializer).Assembly.GetTypes()
.Select(type => (type, attribute: type.GetCustomAttribute<WeArtMiddlewareMessageIDAttribute>()))
.Where(pair => pair.attribute != null && pair.attribute.ID == messageID)
.FirstOrDefault().type;
.FirstOrDefault().type ?? throw new MessageTypeNotFound();

reflectionData = new ReflectionData(messageID, messageType);
_cache[messageID] = reflectionData;
Expand All @@ -168,4 +231,20 @@ public static ReflectionData GetFrom(string messageID)
}
}
}

public class MillisecondsEpochConverter : DateTimeConverterBase
{
private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue(((long)((DateTime)value - _epoch).TotalMilliseconds).ToString());
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null) { return null; }
return _epoch.AddMilliseconds((long)reader.Value);
}
}
}
6 changes: 6 additions & 0 deletions WeArtMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* https://www.weart.it/
*/

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using WeArt.Core;
Expand All @@ -26,6 +27,11 @@ public interface IWeArtMessageCustomSerialization
bool Deserialize(string[] fields);
}

public class WeArtJsonMessage : IWeArtMessage
{
[JsonIgnore]
public DateTime Timestamp { get; set; } = DateTime.Now;
}

/// <summary>
/// Message that requests the middleware to start and to turn on the hardware
Expand Down

0 comments on commit 33558e4

Please sign in to comment.