1

Temporary note: This is NOT a duplicate of the above mentioned post

Let's say I have a server-side class structure like this.

public class Test
{
    // this can be any kind of "Tag"
    public object Data { get; set; }
}

public class Other
{
    public string Test { get; set; }
}

Now a string like this is coming from let's say the client.

{"Data": [{$type: "MyProject.Other, MyProject", "Test": "Test"}] }

When I try to deserialize this into a Test instance, I get a result where the Tag property is a JToken instead of some kind of collection, for example ArrayList or List<object>.

I understand that Json.NET cannot deserialize into a strongly typed list, but I'd expect that it respects that it's at least a list.

Here is my current deserialization code.

var settings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
};

var str = "{\"Data\": [{\"$type\": \"MyProject.Other, MyProject\", \"Test\": \"Test\"}] }";
var test = JsonConvert.Deserialize<Test>(str, settings);

// this first assertion fails
(test.Data is IList).ShouldBeTrue();
(((IList)test.Data)[0] is Other).ShouldBeTrue();

I'm aware of the fact that if I serialize such a structure, then by default I'll get a { $type: ..., $values: [...]} structure in the JSON string instead of a pure array literal, and that will indeed properly deserialize. However, the client is sending a pure array literal, so I should be able to handle that in some way.

8
  • Did you use JsonSerializerSettings and specify typehandling? In any case, if you want to ask "why doesn't my code work" you need to post your code, so post the deserialization code. Commented Nov 23, 2016 at 8:29
  • How should it know that object should be a List<Other> in this case? Should it try to match the received JSON against all fitting types in the assembly? That would be very slow and would not yield any stable result. Commented Nov 23, 2016 at 8:43
  • related stackoverflow.com/questions/21470697/… Commented Nov 23, 2016 at 8:43
  • Possible duplicate of Deserialize Dictionary<string, object> with enum values in C# Commented Nov 23, 2016 at 8:45
  • Edited, removed the Dictionary<string, object> part entirely because my problem is reproducible without that too. Hope now it will get less criticism and more constructive thoughts. Commented Nov 23, 2016 at 8:56

2 Answers 2

1

I managed to put together a JsonConverter to handle these kind of untyped lists. The converter applies when the target type is object. Then if the current token type is array start ([) it will force a deserialization into List<object>. In any other case it will fall back to normal deserialization.

This is a first version which passes my most important unit tests, however as I'm not a Json.NET expert, it might break some things unexpectedly. Please if anyone sees anything what I didn't, leave a comment.

public class UntypedListJsonConverter : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType != JsonToken.StartArray)
        {
            return serializer.Deserialize(reader);
        }

        return serializer.Deserialize<List<object>>(reader);
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(object);
    }
}

Usage example:

var settings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
    Converters = new[] { new UntypedListJsonConverter() }
};

var str = "{\"Data\": [{\"$type\": \"MyProject.Other, MyProject\", \"Test\": \"Test\"}] }";
var test = JsonConvert.Deserialize<Test>(str, settings);
// now these assertions pass
(test.Data is IList).ShouldBeTrue();
(((IList)test.Data)[0] is Other).ShouldBeTrue();
Sign up to request clarification or add additional context in comments.

Comments

0

Try this:

public class Test
{
    public Dictionary<string, List<Other>> Data { get; } = new Dictionary<string, List<Other>>();
}

You need to set up the class you are trying to fill from json data to match as closely to the json structure. From the looks of it, the json looks a dictionary where the keys are strings and the values are arrays of Other objects.

1 Comment

Thank you, of course if I could I would do it this way, but using an object value is intentional. The dictionary is kind of a custom property holder, hence it could contains almost anything.

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.