1

I have the following model structure :

public class Step : ActivityElement
{
    public Step()
    {
        this.Id = Guid.NewGuid();
        this.Label = "Basic Step";
        this.Type = "step";
    }

    public Guid Id { get; set; }
    public string Label { get; set; }
    public Input Input { get; set; }
}

public abstract class Input
{
    public string Type { get; set; }
    public object Value { get; set; }
}

public class GridInput : Input
{
    public GridInput()
    {
        this.Type = "grid";
        this.GridHeader = new GridHeader();
        this.RowList = new List<GridRow>();
    }

    public GridHeader GridHeader { get; set; }
    public List<GridRow> RowList { get; set; }
}

public class GridRow
{
    public GridRow()
    {
        this.IsDeleted = false;
        this.CellList = new List<GridCell>();
    }

    public List<GridCell> CellList { get; set; }
    public bool IsDeleted { get; set; }
}

public class GridCell
{
    public GridCell()
    {
        this.name = "not set yet";
        this.Input = null;
    }

    public string name { get; set; }
    public Input Input { get; set; }
}

Here is the JSON that I am trying to deserialize

{
    "Id":"e833ceae-57e5-4c52-8d56-b047790cb7c7",
    "Label":"Grid time!",
    "Input":
    {
        "GridHeader":{"ColumnDefinitionList":[{"field":"column1","title":"Column 1"},{"field":"column2","title":"Column 2"},{"field":"column3","title":"Column 3"},{"field":"column4","title":"Column 4"}]},
        "RowList":
        [
            {
                "CellList":
                [
                    {"name":"column1","Input":{"Type":"text","Value":"cell 1 row 1"}},
                    {"name":"column2","Input":{"Type":"text","Value":"cell 2 row 1"}},
                    {"name":"column3","Input":{"Type":"text","Value":"cell 3 row 1"}},
                    {"name":"column4","Input":{"Type":"text","Value":"cell 4 row 1"}}
                ],
                "IsDeleted":false
            },
            {
                "CellList":
                [
                    {"name":"column1","Input":{"Type":"text","Value":"cell 1 row 2"}},
                    {"name":"column2","Input":{"Type":"text","Value":"cell 2 row 2"}},
                    {"name":"column3","Input":{"Type":"text","Value":"cell 3 row 2"}},
                    {"name":"column4","Input":{"Type":"text","Value":"cell 4 row 2"}}
                ],
                "IsDeleted":false
            }
        ],
        "Type":"grid",
        "Value":null
    },
    "DisplayPosition":2,
    "Type":"step"
}

Here is the custom deserializer that makes sure the GridInput is the object created as the Input :

public abstract class JsonCreationConverter<T> : JsonConverter
{
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}
public class InputJsonConverter : JsonCreationConverter<Input>
{
    protected override Input Create(Type objectType, JObject jObject)
    {
        var type = (string)jObject.Property("Type");
        switch (type)
        {
            case "grid":
                return new GridInput();
            case "text":
                return new TextInput();
            case "dropdown":
                return new DropdownInput();
            case "checkbox":
                return new CheckboxInput();
        }

        throw new ApplicationException(String.Format("The given type {0} is not supported!", type));
    }
}

I have implemented something so that the GridHeader is skiped (I dont need it to be deserialized, so it simply create an empty GridHeader during the deserializing process), but the RowList end up being always empty after the GridInput is deserialized. I have tried multiple things, but I am running out of idea...

TL:DR: The GridInput is created but the RowList is empty

13
  • 2
    Try json2csharp.com, copy your JSON and that will generate you a matching schema. Compare it with your current schema and see if there is a problem. Commented Apr 22, 2014 at 15:21
  • this.RowList = new List<GridRow>(); Looks like you're making an empty RowList. Commented Apr 22, 2014 at 15:22
  • The json is actually generated from those Model, sent to the view, modified and sent back. All of that using Json.Net Commented Apr 22, 2014 at 15:23
  • @Timothy, without this call, the Rowlist = null, so the problem persist Commented Apr 22, 2014 at 15:23
  • @Zwik You're not showing enough code then. Serializing and deserializing some "plain old C#" classes with List<T> members works perfectly fine on my end. Commented Apr 22, 2014 at 15:24

1 Answer 1

1

I can't reproduce the problem. Is it possible that you forgot to have Json.NET use the InputJsonConverter class? The following code works.

void Main()
{
    var step = JsonConvert.DeserializeObject<Step>(s);
    Console.WriteLine(((GridInput)step.Input).RowList.Count); // 2
}
public class GridHeader { }
public class ActivityElement { public string Type { get; set; } }
public class Step : ActivityElement
{
    public Step()
    {
        this.Id = Guid.NewGuid();
        this.Label = "Basic Step";
        this.Type = "step";
    }

    public Guid Id { get; set; }
    public string Label { get; set; }
    public Input Input { get; set; }
}
[JsonConverter(typeof(InputJsonConverter))]
public abstract class Input
{
    public string Type { get; set; }
    public object Value { get; set; }
}

public class TextInput : Input
{
    public TextInput()
    {
        this.Type = "text";
    }
}
public class GridInput : Input
{
    public GridInput()
    {
        this.Type = "grid";
        this.GridHeader = new GridHeader();
        this.RowList = new List<GridRow>();
    }

    public GridHeader GridHeader { get; set; }
    public List<GridRow> RowList { get; set; }
}

public class GridRow
{
    public GridRow()
    {
        this.IsDeleted = false;
        this.CellList = new List<GridCell>();
    }

    public List<GridCell> CellList { get; set; }
    public bool IsDeleted { get; set; }
}

public class GridCell
{
    public GridCell()
    {
        this.name = "not set yet";
        this.Input = null;
    }

    public string name { get; set; }
    public Input Input { get; set; }
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
    public override void WriteJson(
    JsonWriter writer,
    Object value,
    JsonSerializer serializer
)
    {
        throw new NotImplementedException();
    }
}
public class InputJsonConverter : JsonCreationConverter<Input>
{
    protected override Input Create(Type objectType, JObject jObject)
    {
        var type = (string)jObject.Property("Type");
        switch (type)
        {
            case "grid":
                return new GridInput();
            case "text":
                return new TextInput();
//            case "dropdown":
//                return new DropdownInput();
//            case "checkbox":
//                return new CheckboxInput();
        }

        throw new ApplicationException(String.Format("The given type {0} is not supported!", type));
    }
}
string s = @"{
    ""Id"":""e833ceae-57e5-4c52-8d56-b047790cb7c7"",
    ""Label"":""Grid time!"",
    ""Input"":
    {
        ""GridHeader"":{""ColumnDefinitionList"":[{""field"":""column1"",""title"":""Column 1""},{""field"":""column2"",""title"":""Column 2""},{""field"":""column3"",""title"":""Column 3""},{""field"":""column4"",""title"":""Column 4""}]},
        ""RowList"":
        [
            {
                ""CellList"":
                [
                    {""name"":""column1"",""Input"":{""Type"":""text"",""Value"":""cell 1 row 1""}},
                    {""name"":""column2"",""Input"":{""Type"":""text"",""Value"":""cell 2 row 1""}},
                    {""name"":""column3"",""Input"":{""Type"":""text"",""Value"":""cell 3 row 1""}},
                    {""name"":""column4"",""Input"":{""Type"":""text"",""Value"":""cell 4 row 1""}}
                ],
                ""IsDeleted"":false
            },
            {
                ""CellList"":
                [
                    {""name"":""column1"",""Input"":{""Type"":""text"",""Value"":""cell 1 row 2""}},
                    {""name"":""column2"",""Input"":{""Type"":""text"",""Value"":""cell 2 row 2""}},
                    {""name"":""column3"",""Input"":{""Type"":""text"",""Value"":""cell 3 row 2""}},
                    {""name"":""column4"",""Input"":{""Type"":""text"",""Value"":""cell 4 row 2""}}
                ],
                ""IsDeleted"":false
            }
        ],
        ""Type"":""grid"",
        ""Value"":null
    },
    ""DisplayPosition"":2,
    ""Type"":""step""
}";
Sign up to request clarification or add additional context in comments.

2 Comments

Na, if the InputJsonConverter isnt use, there is an abstract object deserialzing error that is raised. But the fact that you didnt reproduce it with this code makes me wonder a couple of things on my side, so I'll check and see. Thank you tim!
Ok I found the culprit, it was the deserialization of GridHeader that was somehow canceling the deserialization of the RowList... Found it because of your test, thank you very much Tim :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.