0

This is a stretch, but it's a real world example, yet fluffified.. We have a Data node with multiple Fluff objects in a list. They are named according to their index in the list, as such: { Fluff_0, Fluff_1, ..., Fluff_n }

When I deserialize this object I would want it to be deserialized into a List<Fluff>.

Is there a way to decorate this with JsonPropertyAttributes so that I would get an ordered generic List (or other collection) of Fluff objects in the Data object?

[TestFixture]
public class FluffDeserializationTests
{
    [Test]
    public void FluffDeserialization()
    {
        var json = "{\"Data\": { \"Fluff_0\": {\"ID\": \"abc\"}, \"Fluff_1\": { \"ID\": \"abd\" } } }";
        var result = JsonConvert.DeserializeObject<Fluff>(json);
        Assert.That(result.Data.Things != null);
        Assert.That(result.Data.Things.Count, Is.EqualTo(2));
        Assert.That(result.Data.Things[0].ID, Is.EqualTo("abc"));
        Assert.That(result.Data.Things[1].ID, Is.EqualTo("abd"));
    }


    public class Fluff
    {
        public Data Data { get; set; }
    }

    public class Data
    {
        public List<Thing> Things { get; set; }
    }

    public class Thing
    {
        public string ID { get; set; }
    }
}
2
  • Your "Data" container is a JSON object, which the standard defines as an unordered set of name/value pairs. So, what order do you expect the list to be in after deserialization? Commented Nov 8, 2015 at 18:41
  • In the order they occurred in the json string @dbc. Commented Nov 8, 2015 at 19:34

2 Answers 2

4

There is not an attribute in Json.Net that will directly convert your fluffy object into a list, but you can make a custom JsonConverter to do it without too much trouble, then decorate your Data class with an attribute to tell Json.Net to use the converter.

Here is what the converter might look like. Note that if there happen to be any gaps in the sequence of Fluff_n keys, this code will still maintain their relative order in the resulting list, but the gaps will not be preserved. I'm guessing this won't be an issue, but just FYI.

class FluffDataConverter : JsonConverter
{
    readonly int PrefixLength = "Fluff_".Length;

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        List<Thing> things = jo.Properties()
                               .OrderBy(p => int.Parse(p.Name.Substring(PrefixLength)))
                               .Select(p => p.Value.ToObject<Thing>())
                               .ToList();
        return new Data { Things = things };
    }

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

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

To use this converter, just place a [JsonConverter] attribute on your Data class like this:

[JsonConverter(typeof(FluffDataConverter))]
public class Data
{
    public List<Thing> Things { get; set; }
}

When you re-rerun your test code, it should work as you described.

Sign up to request clarification or add additional context in comments.

2 Comments

Legend, definitely what I was after! The way I solved it in the end was to iterate over all of the inner objects, and also make my converter generic, unfortunately I can't post that comment here since I couldn't get it to format the code:). Your solution was the first one that came in though, so I'll give you the kudos! Thanks!
No problem; happy to help. FYI, you can post an answer to your own question if you want to share your ultimate solution. Comments are "second class citizens" on this site so they are intentionally limited in what you can put in them.
0

You can try this.

var things = new List<Thing>();
var json = "{\"Data\": { \"Fluff_0\": {\"ID\": \"abc\"}, \"Fluff_1\": { \"ID\": \"abd\" } } }";
JObject jObject = JObject.Parse(json);
var data = (JObject)jObject["Data"];  
var prop = data.Properties().Where(p => p.Name.Contains("Fluff")).Count();
if (prop > 0)
  for (int i = 0; i < prop; i++)
    things.Add(new Thing { ID = (string)((JObject)data["Fluff_" + i])["ID"]});  //please check if get null value

1 Comment

Problem is that it's very hard to make that generic. In the API that I'm working with there's a lot of examples of this way of generating lists, so I really needed to use a converter for this.

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.