The problem
I am receiving a JSON-package from a server in the format {"A": 'Pelle', "B": 55, "C": 5.5} that needs to be mapped to an entity class in the format:
class EntityAttributes
{
public string Name { get; set; }
public int Level { get; set; }
public float Strength { get; set; }
}
The attribute names in the JSON, "A", "B", and "C" should be mapped according to the following class:
class Constants
{
public const byte NAME = 65; // ASCII "A"
public const byte LEVEL = 66; // ASCII "B"
public const byte STRENGTH = 67; // ASCII "C"
}
where 65 is the ASCII byte representation of "A", 66 of "B" and 67 of "C".
Where I am stuck (I have no idea if this is the right way to go):
I have been trying to use the JSON NET FOR UNITY which allows you to deserialize the JSON into a class using custom names:
class EntityAttributes
{
[[JsonProperty("A")]]
public string Name { get; set; }
[[JsonProperty("B")]]
public int Level { get; set; }
[[JsonProperty("C")]]
public float Strength { get; set; }
}
However, I do not want to hardcode "A", "B" and "C" in the annotation since these might change in the future. It is also not possible (afaik) to convert from byte to string in a decorator since only constant expressions are allowed.
Any idea how I should tackle this problem?
It sounds a bit like the main issue is your field names having to be constant - what you don't want.
Since your attributes are simple/basic enough you could instead use SimpleJson that was written by someone in the Units community long ago ;) You simply create that file anywhere in your project.
The big advantage: it can use completely dynamic field names so you can easily make it e.g.
public static class Constants
{
public const byte NAME = 65;
public const byte LEVEL = 66;
public const byte STRENGTH = 67;
}
and then do e.g.
class EntityAttributes
{
public readonly string Name {get;set;};
public readonly int Level {get;set};
public readonly float Strength {get;set};
public EntityAttributes(string name, int level, float strength)
{
Name = name;
Level = level;
Strength = strength;
}
}
Now wherever you originally parsed your JSON directly into that type you won't anymore. Instead you do something like e.g.
var root = JSON.Parse(jsonString);
var item = new EntityAttributes (
root[Encoding.ASCII.GetString(new [](Constants.NAME})].Value,
root[Encoding.ASCII.GetString(new []{Constants.LEVEL})].AsInt(),
root[Encoding.ASCII.GetString(new []{Constants.STRENGTH})].AsFloat()
);
Note though that either way of course you still have to set these constant byte values correctly.
So in my personal opinion I still think you could/should as well simply use
public static class Constants
{
public const string NAME = "A";
public const string LEVEL = "B";
public const string STRENGTH = "C";
}
Then you wouldn't even have that trouble. Either your server has to provide whatever structure the client expects or the other way round the client has to deal with whatever the server provides. Either way if there are changes made in the one side, the other side has to be changed as well.
Note: Typed on smartphone but I hope the idea gets clear
You could create a custom JsonConverter to solve this problem:
class EntityAttributesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(EntityAttributes);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
return new EntityAttributes
{
Name = GetValue<string>(jo, Constants.NAME),
Level = GetValue<int>(jo, Constants.LEVEL),
Strength = GetValue<float>(jo, Constants.STRENGTH)
};
}
private static T GetValue<T>(JObject jo, byte b)
{
JToken val = jo[Encoding.ASCII.GetString(new byte[] { b })];
return val != null ? val.ToObject<T>() : default(T);
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then just mark your EntityAttributes class with a [JsonConverter] attribute like this:
[JsonConverter(typeof(EntityAttributesConverter))]
class EntityAttributes
{
public string Name { get; set; }
public int Level { get; set; }
public float Strength { get; set; }
}
And deserialize as you normally would:
var attributes = JsonConvert.DeserializeObject<EntityAttributes>(json);
Here's a working demo (in a console application): https://dotnetfiddle.net/VPfbdy
Use JsonUtility.FromJson to deserialize a JSON file and simple convert to System.Text.Encoding.UTF8.GetBytes(myString) if you want byte.