1

I have a JSON text, from a search in Sharepoint 2013, and would like to deserialize it into a list of MyClass. I have a solution using KeyValuePair

var results = item["Cells"]["results"].ToObject<List<KeyValuePair<string, string>>>();

But this method removes date formats etc. Everything ends up as string. Eg. a correct Norwegian date in the JSON file ends up as a quasi Norwenglish date in text.

Edit: I'm using the Newtonsoft.Json library.

Edit: Date format is only part of the problem. The deserializing is the main problem for me.

The JSON and class are simplified for this example. The JSON file can have many records like this so it will end up in a list of objects. Do you have a good solution to deserialize this JSON?

JSON

{
  "__metadata": {
    "type": "SP.SimpleDataRow"
  },
  "Cells": {
    "results": [
      {
        "__metadata": {
          "type": "SP.KeyValue"
        },
        "Key": "Id",
        "Value": "358553",
        "ValueType": "Edm.Int64"
      },
      {
        "__metadata": {
          "type": "SP.KeyValue"
        },
        "Key": "Url",
        "Value": "http://somewhere.com",
        "ValueType": "Edm.String"
      },
      {
        "__metadata": {
          "type": "SP.KeyValue"
        },
        "Key": "Title",
        "Value": "My title",
        "ValueType": "Edm.String"
      },
      {
        "__metadata": {
          "type": "SP.KeyValue"
        },
        "Key": "MyDate",
        "Value": "2017-09-10T11:10:19Z",
        "ValueType": "Edm.DateTime"
      }
    ]
  }
}

Class

public class MyClass
{
    public int Id { get; set; }
    public string Url { get; set; }
    public string Title { get; set; }
    public DateTime MyDate { get; set; }
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
Srednasoft
  • 43
  • 5
  • What about using a proper Json library? The common one used in NET is Json.Net – Steve Aug 31 '17 at 10:48
  • Sorry, forgot to inform about the Json library at use. I'm using the Newtonsoft.Json library. – Srednasoft Aug 31 '17 at 10:51
  • You need to specify a `IsoDateTimeConverter` for whatever your date format is. Json.Net expects [ISO 8601 dates](https://www.iso.org/iso-8601-date-and-time-format.html) – Liam Aug 31 '17 at 11:01
  • Possible duplicate of [Deserializing dates with dd/mm/yyyy format using Json.Net](https://stackoverflow.com/questions/21256132/deserializing-dates-with-dd-mm-yyyy-format-using-json-net) – Liam Aug 31 '17 at 11:01
  • Also worth reading [The “right” JSON date format](https://stackoverflow.com/questions/10286204/the-right-json-date-format). Technically your json date is invalid as it does not conform to the Javascript standard (8601). It's just a string that happens to represent a date where you come from – Liam Aug 31 '17 at 11:04
  • The date in the JSON is nothing I can change. It is the search result. The main problem is getting the JSON deserialized into a list of objects. – Srednasoft Aug 31 '17 at 11:14

1 Answers1

1

You can deserialize the "results" array inside the JSON with the following converter:

public class KeyValuePropertyArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var pairs = serializer.Deserialize<List<KeyValuePair<string, JToken>>>(reader);
        var jObj = new JObject(pairs.Select(p => new JProperty(p.Key, p.Value)));
        existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        serializer.Populate(jObj.CreateReader(), existingValue);
        return existingValue;
    }

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

Then define MyClass as follows:

public class MyClass
{
    public long Id { get; set; } // This should be long not int
    public string Url { get; set; }
    public string Title { get; set; }
    public DateTime MyDate { get; set; }
}

Then, deserialize in two steps as shown below:

// Disable date recognition while parsing intermediate JToken representation as shown in
// https://stackoverflow.com/questions/35138346/jtoken-get-raw-original-json-value/35141787
var item = JsonConvert.DeserializeObject<JObject>(jsonString, new JsonSerializerSettings { DateParseHandling = DateParseHandling.None });

var finalSettings = new JsonSerializerSettings
{
    Converters = { new KeyValuePropertyArrayConverter<MyClass>() /*, Add some appropriate IsoDateTimeConverter if required, */ },
    // Or set DateFormatString if required
};
var myClass = item["Cells"]["results"].ToObject<MyClass>(JsonSerializer.CreateDefault(finalSettings));

Notes:

  1. Your JSON only corresponds to a single instance of MyClass -- not an array of them. The only array in the JSON is the array of property values.

  2. The JSON specifies that "Id" is "Edm.Int64" so I modified MyClass.Id to be a long.

  3. By setting DateParseHandling = DateParseHandling.None when loading the initial JObject, you defer date string recognition until final deserialization, when you can apply an appropriate IsoDateTimeConverter or DateFormatString, and Json.NET can make use of type information in the final target class for help in recognizing dates embedded in the JSON.

  4. I did not implement serialization to the same format, as your question did not request this.

Sample .Net fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thnx, this looks great :-) The JSON example is part of an array. It could be only one record or more. Depending on the search result from SharePoint. I will test this now. – Srednasoft Sep 01 '17 at 06:53