0

I'm working on a project with friends and I'm doing a composite pattern. I have a problem deserializing my json to my composite object. I managed to serialize it, so I will try to give as much info as possible.

My composite classes:

    public abstract class Component
    {
        [JsonProperty("name")]
        public string name;

        public Component(string name)
        {
            this.name = name;
        }

        public virtual void Add(Component component)
        {
            throw new NotImplementedException();
        }

        public virtual void Remove(Component component)
        {
            throw new NotImplementedException();
        }

        public virtual bool IsComposite()
        {
            return true;
        }
    }

    [JsonConverter(typeof(CompositeConverter))]
    public class Composite : Component
    {
        [JsonProperty("children")]
        public List<Component> _children { get; set; }

        public Composite(string name) : base(name)
        {
            this._children = new List<Component>();
        }

        public override void Add(Component component)
        {
            this._children.Add(component);
        }

        public override void Remove(Component component)
        {
            this._children.Remove(component);
        }
    }

    public class Leaf : Component
    {
        public int experience;
        public bool achieved;

        [JsonConstructor]
        public Leaf(string name, int experience) : base(name)
        {
            this.experience = experience;
        }

        public override bool IsComposite()
        {
            return false;
        }
    }

So, with these classes I'm able to create what I want:

            Composite levels = new Composite("Levels");
            Composite firstLevel = new Composite("First level");
            Composite secondLevel = new Composite("Second level");
            Composite thirdLevel = new Composite("Third level");
            Leaf firstAchievement = new Leaf("Mission 1", 1);
            Leaf secondAchviement = new Leaf("Mission 2", 2);
            Leaf thirdAchievement = new Leaf("Mission 3", 3);
            Leaf fourthAchievement = new Leaf("Mission 4", 4);
            Leaf fifthAchievement = new Leaf("Mission 5", 5);
            Leaf sixthAchievement = new Leaf("Mission 6", 6);
            firstLevel.Add(firstAchievement);
            secondLevel.Add(secondAchviement);
            secondLevel.Add(thirdAchievement);
            thirdLevel.Add(fourthAchievement);
            thirdLevel.Add(fifthAchievement);
            thirdLevel.Add(sixthAchievement);
            levels.Add(firstLevel);
            levels.Add(secondLevel);
            levels.Add(thirdLevel);

Now the most interesting part begins. So I have composite object, when I serialize it with my own serialiser:

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Composite composite = (Composite)value;

            PropertyInfo prop = typeof(Composite).GetProperty("_children");
            List<Component> children = (List<Component>)prop.GetValue(composite);

            JArray array = new JArray();
            foreach (Component e in children)
            {
                array.Add(JToken.FromObject(e, serializer));
            }

            JObject obj = new JObject();
            obj.Add(composite.name, array);
            obj.WriteTo(writer);
        }

I get this json:

{
  "Levels": [
    {
      "First level": [
        {
          "experience": 1,
          "achieved": false,
          "name": "Mission 1"
        }
      ]
    },
    {
      "Second level": [
        {
          "experience": 2,
          "achieved": false,
          "name": "Mission 2"
        },
        {
          "experience": 3,
          "achieved": false,
          "name": "Mission 3"
        }
      ]
    },
    {
      "Third level": [
        {
          "experience": 4,
          "achieved": false,
          "name": "Mission 4"
        },
        {
          "experience": 5,
          "achieved": false,
          "name": "Mission 5"
        },
        {
          "experience": 6,
          "achieved": false,
          "name": "Mission 6"
        }
      ]
    }
  ]
}

Everything looks perfect and now I need to deserialize it and I'm just banging my head to the wall. Maybe someone has some hints or something on how should I approach it? Wasn't able to find any good resources on this

Povilas Dirse
  • 137
  • 1
  • 10
  • Looks like you are overthinking this. Perhaps you should just use normal serialization (you could use a `IDictionary` property in order to get dynamic key names for the children) – Charlieface Nov 25 '21 at 09:45
  • There doesn't seem to be anything in your JSON to indicate what type of `Component` each object represents. Your `Composite` class's children could be `Leaf` instances, `Composite` instances, or any other type derived from `Component`. – Richard Deeming Nov 25 '21 at 09:45
  • I wrote an extensive answer here, since I couldnt find a working solution myself to this problem: https://stackoverflow.com/questions/70619208/how-to-deserialize-json-with-composite-pattern-c-json/70619209#70619209 – Dodo Jan 07 '22 at 09:42

1 Answers1

0

Had a similar problem before. I think I found that post Deserializing JSON to abstract class

I changed it a little bit that it could work in a more generic.

Disclaimer: Not sure if it works directly like this just edited the code here a little bit and didn't used an ide to change it, but should look something like it.

Isn't pretty that the type shows up in the json but it makes it easy to just inherit your base class from the base class

public class BaseConverter<T> : JsonConverter where T : JsonBaseClass
{
    static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(T)); //not sure what i wrote here maybe the check is wrong. You have to check if it inherits from the base class...
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        var type = Type.GetType(jo["JsonStoreType"]);
        return JsonConvert.DeserializeObject(jo.ToString(),type);
       
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException(); // won't be called because CanWrite returns false
    }
}

[JsonConverter(typeof(BaseConverter<JsonBaseClass>)]
public class JsonBaseClass
{
public string JsonStoreType = GetType().ToString()

}
Mucksh
  • 160
  • 5
  • I wrote an extensive answer here: https://stackoverflow.com/questions/70619208/how-to-deserialize-json-with-composite-pattern-c-json/70619209#70619209 – Dodo Jan 07 '22 at 09:41
  • 1
    Hey, I found a working answer for myself. If you're interested I could post it on here. Maybe I should post it here if someone else has similar problem? – Povilas Dirse Jan 09 '22 at 17:18