C
C#•4d ago
bvko

Custom JsonConverter to apply to interface or abstract class, anyone ?

// public class ISerilizableStateConverter : JsonConverter <<< I need to write some custom Converter that will when doing
// serializing take Type of implementing class and put it in Json, example ResolveToType:"TestClass1" and when de-serializing
// read that ResolveToType and create instance of that implementing class
// So if i have ISerilizableState currentState i can apply serializing and de-serializing and not get error, atm i get
// recursion error or endelss loop or null xD, anyone already tacked this dragon?

public interface ISerilizableState // can be abstract class too
{
void OnEnter();
void OnExit();
void OnTick();
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass1 : ISerilizableState
{
public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test1"); }
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass2 : ISerilizableState
{
public string RandomString = "sss";

public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test2"); }
}
// public class ISerilizableStateConverter : JsonConverter <<< I need to write some custom Converter that will when doing
// serializing take Type of implementing class and put it in Json, example ResolveToType:"TestClass1" and when de-serializing
// read that ResolveToType and create instance of that implementing class
// So if i have ISerilizableState currentState i can apply serializing and de-serializing and not get error, atm i get
// recursion error or endelss loop or null xD, anyone already tacked this dragon?

public interface ISerilizableState // can be abstract class too
{
void OnEnter();
void OnExit();
void OnTick();
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass1 : ISerilizableState
{
public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test1"); }
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass2 : ISerilizableState
{
public string RandomString = "sss";

public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test2"); }
}
7 Replies
glhays
glhays•4d ago
Can you provide an example JSON structure you expect after serialization, including the attributes that should be included in the JSON?
bvko
bvkoOP•4d ago
sure, in case of TestClass2 my logic would be { "ResolveToType ":"TestClass2" "RandomString": "sss" } and when i de-serialize i want to get TestClass2 🙂 cuz i ISerilizableStateConverter JsonRead should use its ResolveToType for that @glhays any magic solution incoming?
glhays
glhays•4d ago
Something maybe like this.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public class ISerilizableStateConverter : JsonConverter<ISerilizableState>
{
public override ISerilizableState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (JsonDocument doc = JsonDocument.ParseValue(ref reader))
{
JsonElement root = doc.RootElement;
string typeName = root.GetProperty("ResolveToType").GetString();
Type type = Type.GetType(typeName);
if (type == null)
{
throw new JsonException($"Unknown type: {typeName}");
}
return (ISerilizableState)JsonSerializer.Deserialize(root.GetRawText(), type, options);
}
}

public override void Write(Utf8JsonWriter writer, ISerilizableState value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("ResolveToType", value.GetType().FullName);
foreach (var property in value.GetType().GetProperties())
{
var propValue = property.GetValue(value);
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, propValue, property.PropertyType, options);
}
writer.WriteEndObject();
}
}
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public class ISerilizableStateConverter : JsonConverter<ISerilizableState>
{
public override ISerilizableState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (JsonDocument doc = JsonDocument.ParseValue(ref reader))
{
JsonElement root = doc.RootElement;
string typeName = root.GetProperty("ResolveToType").GetString();
Type type = Type.GetType(typeName);
if (type == null)
{
throw new JsonException($"Unknown type: {typeName}");
}
return (ISerilizableState)JsonSerializer.Deserialize(root.GetRawText(), type, options);
}
}

public override void Write(Utf8JsonWriter writer, ISerilizableState value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("ResolveToType", value.GetType().FullName);
foreach (var property in value.GetType().GetProperties())
{
var propValue = property.GetValue(value);
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, propValue, property.PropertyType, options);
}
writer.WriteEndObject();
}
}
[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass1 : ISerilizableState
{
public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test1"); }
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass2 : ISerilizableState
{
public string RandomString = "sss";

public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test2"); }
}
[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass1 : ISerilizableState
{
public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test1"); }
}

[JsonConverter(typeof(ISerilizableStateConverter))]
public class TestClass2 : ISerilizableState
{
public string RandomString = "sss";

public void OnEnter() { }
public void OnExit() { }
public void OnTick() { UnityEngine.Debug.LogError("Test2"); }
}
ISerilizableState state = new TestClass1();
string json = JsonSerializer.Serialize(state);
Console.WriteLine(json);

ISerilizableState deserializedState = JsonSerializer.Deserialize<ISerilizableState>(json);
deserializedState.OnTick();
ISerilizableState state = new TestClass1();
string json = JsonSerializer.Serialize(state);
Console.WriteLine(json);

ISerilizableState deserializedState = JsonSerializer.Deserialize<ISerilizableState>(json);
deserializedState.OnTick();
bvko
bvkoOP•4d ago
will try ty you are god if this works 🙂 atm my soution with propertis gives me loop expection this is not using using Newtonsoft.Json; ? does this work with using Newtonsoft.Json;? Or does anyone know how to for for specific class to turn typehanding.object so i can serilize/deserlize classes that implement some abstract class
lycian
lycian•4d ago
I think the easiest way if you have an abstract base is to have either an int or enum for representing the type id and doing something like this https://stackoverflow.com/questions/20995865/deserializing-json-to-abstract-class
Stack Overflow
Deserializing JSON to abstract class
I am trying to deserialize a JSON string to a concrete class, which inherits from an abstract class, but I just can't get it working. I have googled and tried some solutions but they don't seem to ...
lycian
lycian•4d ago
Although I would recommend switching to System.Text.Json if you can
bvko
bvkoOP•2d ago
Hmm is there any way for jonSerializer when deserilize to call it whitout <T> and for it to know to use custom deserilizer
Want results from more Discord servers?
Add your server