How to create JSON arrays of “jagged” objects from a .NET data structure

Deadly 提交于 2019-12-08 05:44:20

问题


As a result of a LINQ-to-Entities projection, I end up with a List<ChartDataRecord> that looks like the following had I created it manually:

List<ChartDataRecord> data = new List<ChartDataRecord>();
data.Add(new ChartDataRecord { date = 1370563200000, graph = "g0", value = 70 });
data.Add(new ChartDataRecord { date = 1370563200000, graph = "g1", value = 60 });
data.Add(new ChartDataRecord { date = 1370563200000, graph = "g2", value = 100 });
data.Add(new ChartDataRecord { date = 1370563260000, graph = "g0", value = 71 });
data.Add(new ChartDataRecord { date = 1370563260000, graph = "g2", value = 110 });
data.Add(new ChartDataRecord { date = 1370563320000, graph = "g0", value = 72 });
data.Add(new ChartDataRecord { date = 1370563320000, graph = "g1", value = 62 });
data.Add(new ChartDataRecord { date = 1370563320000, graph = "g2", value = 150 });

The charting framework I am using (amCharts) requires that the JSON data provider be formatted exactly as follows:

{
   "data": [
      { "date": 1370563200000, "g0": 70, "g1": 60, "g2": 100 },
      { "date": 1370563260000, "g0": 71, "g2": 110 },
      { "date": 1370563320000, "g0": 72, "g1": 62, "g2": 150 }
   ],
   ...other chart properties
}

What is the recommended approach for serializing the sample List<ChartDataRecord> into this JSON structure? Is there is something in the Json.NET framework that can make this fairly easy? If not, how would this be done manually? Should I use a more dynamic data structure for the List<ChartDataRecord> and try to populate it using my LINQ-to-Entities query?


回答1:


You can do this with a custom converter like so:

class ChartDataRecordCollectionConverter : JsonConverter
{
    /// <summary>
    /// Writes the JSON representation of the object.
    /// </summary>
    /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param><param name="value">The value.</param><param name="serializer">The calling serializer.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var set = (ChartDataRecordCollection) value;

        writer.WriteStartObject();
        writer.WritePropertyName("data");
        writer.WriteStartArray();

        //Group up the records in the collection by the 'date' property
        foreach (var record in set.GroupBy(x => x.date))
        {
            writer.WriteStartObject();

            writer.WritePropertyName("date");
            writer.WriteValue(record.Key);

            //Write the graph/value pairs as properties and values
            foreach (var part in record)
            {
                writer.WritePropertyName(part.graph);
                writer.WriteValue(part.value);
            }

            writer.WriteEndObject();
        }

        writer.WriteEndArray();
        writer.WriteEndObject();
    }

    /// <summary>
    /// Reads the JSON representation of the object.
    /// </summary>
    /// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param><param name="objectType">Type of the object.</param><param name="existingValue">The existing value of object being read.</param><param name="serializer">The calling serializer.</param>
    /// <returns>
    /// The object value.
    /// </returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var result = new ChartDataRecordCollection();
        var obj = JObject.Load(reader);
        var container = obj["data"];

        //Examine each object in the array of values from the result
        foreach (JObject item in container)
        {
            //Get and store the date property
            var date = item["date"].Value<long>();

            //For each property that is not the date property on the object, construct a
            //  ChartDataRecord with the appropriate graph/value pair
            foreach (var property in item.Properties())
            {
                if (property.Name == "date")
                {
                    continue;
                }

                result.Add(new ChartDataRecord
                {
                    date = date,
                    graph = property.Name,
                    value = item[property.Name].Value<int>()
                });
            }
        }

        return result;
    }

    /// <summary>
    /// Determines whether this instance can convert the specified object type.
    /// </summary>
    /// <param name="objectType">Type of the object.</param>
    /// <returns>
    /// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
    /// </returns>
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (ChartDataRecordCollection);
    }
}

This converter will operate on a collection type wrapping List<ChartDataRecord> defined like so

[JsonConverter(typeof(ChartDataRecordCollectionConverter))]
public class ChartDataRecordCollection : List<ChartDataRecord>
{
    public ChartDataRecordCollection()
    {
    }

    public ChartDataRecordCollection(IEnumerable<ChartDataRecord> records)
    {
        AddRange(records);
    }
}

Usage example (and proof of correctness):

public class ChartDataRecord
{
    public long date { get; set; }
    public string graph { get; set; }
    public int value { get; set; }

    public override bool Equals(object obj)
    {
        var o = (ChartDataRecord) obj;
        return o.date == date && o.graph == graph && o.value == value;
    }
}

...

static void Main(string[] args)
{
    var data = new List<ChartDataRecord>
    {
        new ChartDataRecord { date = 1370563200000, graph = "g0", value = 70 },
        new ChartDataRecord { date = 1370563200000, graph = "g1", value = 60 },
        new ChartDataRecord { date = 1370563200000, graph = "g2", value = 100 },
        new ChartDataRecord { date = 1370563260000, graph = "g0", value = 71 },
        new ChartDataRecord { date = 1370563260000, graph = "g2", value = 110 },
        new ChartDataRecord { date = 1370563320000, graph = "g0", value = 72 },
        new ChartDataRecord { date = 1370563320000, graph = "g1", value = 62 },
        new ChartDataRecord { date = 1370563320000, graph = "g2", value = 150 }
    };

    var records = new ChartDataRecordCollection(data);

    var result = JsonConvert.SerializeObject(records);
    Console.WriteLine(result);
    var test = JsonConvert.DeserializeObject<ChartDataRecordCollection>(result);
    Console.WriteLine(records.SequenceEqual(test));
    Console.ReadLine();
}

Outputs:

{"data":[{"date":1370563200000,"g0":70,"g1":60,"g2":100},{"date":1370563260000,"g0":71,"g2":110},{"date":1370563320000,"g0":72,"g1":62,"g2":150}]}
true



回答2:


Yes, JSON.NET can handle it quite easily. Just add the list into another object's "data" property, and serialize it. The example below uses an anonymous type for the "wrapper" object, but a "normal" CLR type would work just as well.

public class StackOverflow_17012831
{
    public class ChartDataRecord
    {
        public long date { get; set; }
        public string graph { get; set; }
        public int value { get; set; }
    }
    public static void Test()
    {
        List<ChartDataRecord> data = new List<ChartDataRecord>();
        data.Add(new ChartDataRecord { date = 1370563200000, graph = "g0", value = 70 });
        data.Add(new ChartDataRecord { date = 1370563200000, graph = "g1", value = 60 });
        data.Add(new ChartDataRecord { date = 1370563200000, graph = "g2", value = 100 });
        data.Add(new ChartDataRecord { date = 1370563260000, graph = "g0", value = 71 });
        data.Add(new ChartDataRecord { date = 1370563260000, graph = "g2", value = 110 });
        data.Add(new ChartDataRecord { date = 1370563320000, graph = "g0", value = 72 });
        data.Add(new ChartDataRecord { date = 1370563320000, graph = "g1", value = 62 });
        data.Add(new ChartDataRecord { date = 1370563320000, graph = "g2", value = 150 });

        var obj = new { data = data, name = "Name" };
        string str = JsonConvert.SerializeObject(obj);
        Console.WriteLine(str);
    }
}


来源:https://stackoverflow.com/questions/17012831/how-to-create-json-arrays-of-jagged-objects-from-a-net-data-structure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!