0

I get the following json string format from an API:

{
   "Items":[
      {
         "Item":{
            "Codes":{},
            "Date":1523539700000,
            "Name":"Item1",
            "Price":"15"
         }
      },
      {
         "Item":{
            "Codes":{
               "productcode":"a",
               "departmentcode":"b",
               "storecode":"c"
            },
            "Date":1523537700000,
            "Name":"Item2",
            "Price":"20"
         }
      },
      {
         "Item":{
            "Codes":{
               "productcode":"a",
               "departmentcode":"b",
               "storecode":"c"
            },
            "date":1523539700000,
            "name":"Item3",
            "price":"30"
         }
      }
   ]
}

I am not interested in the Codes element and then created the following class:

public class Item
    {
        [JsonConverter(typeof(ConvertItemDateTime))]
        [JsonProperty(PropertyName = "dateReceived")]
        public DateTime DateReceived { get; set; }
        [JsonProperty(PropertyName = "name")]
        public string Name{ get; set; }
        [JsonProperty(PropertyName = "price")]
        public double Price{ get; set; }
        public IList<Items> itemListResults { get; set; } //For view
        public int productid; // Added for view
    }

I then try an parse it...

JObject jsondata = JObject.Parse(responseToString(response))
var values = json.GetValue("Items");

...which by this point I can see that variable 'values' still contains each value (Date, Name, Price) inside each item. Then I try and deserialize it...

IList<Items> itemList = new List<Items>();
itemList = JsonConvert.DeserializeObject<IList<Items>>(values.ToString());

When I debug from this point onward I can see a list with the exact number of items I am expecting but all the values (Date, Name, Price) are null. The Date value inside the json will change (from the API side). Sometimes it will be a long and sometimes a string date format containing a "[GMT]".

My ConvertItemDateTime method is as follow:

public class ConvertItemDateTime : JsonConverter
    {
        private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        public override bool CanConvert(Type objectType)
        {
            Console.WriteLine(objectType);
            return true;
        }
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Console.WriteLine(value.GetType());
            long ticks;
            if (value is DateTime)
            {
                var dateTime = (DateTime)value;
                ticks = (long)(dateTime.ToUniversalTime() - UnixEpoch).TotalMilliseconds;
            }
            else if (value is DateTimeOffset)
            {
                var dateTimeOffset = (DateTimeOffset)value;
                ticks = (long)(dateTimeOffset.ToUniversalTime() - UnixEpoch).TotalMilliseconds;
            }
            else
            {
                throw new JsonSerializationException("Expected date object value.");
            }
            if (ticks < 0)
            {
                throw new JsonSerializationException("Cannot convert date value that is before Unix epoch of 00:00:00 UTC on 1 January 1970.");
            }
            writer.WriteValue(ticks);
        }
        public override bool CanRead
        {
            get { return true; }
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            long ticks;
            if (reader.TokenType == JsonToken.Integer)
            {
                ticks = (long)reader.Value;
            }
            else if (reader.TokenType == JsonToken.String)
            {
                var stringValue = reader.Value.ToString();
                if (stringValue.Contains("[GMT]"))
                {
                    stringValue = stringValue.Substring(0, stringValue.Length - 6);
                    DateTime createdDate = DateTime.Parse(stringValue);
                    return createdDate;
                }

                if (!long.TryParse((string)reader.Value, out ticks))
                {
                    throw new JsonSerializationException("Cannot convert invalid value.");
                }
            }
            else
            {
                throw new JsonSerializationException("Unexpected token parsing date.");
            }
            if (ticks >= 0)
            {
                DateTime d = UnixEpoch.AddMilliseconds(ticks);
                Type t = objectType;
                if (t == typeof(DateTimeOffset))
                {
                    return new DateTimeOffset(d, TimeSpan.Zero);
                }
                return d;
            }
            else
            {
                throw new JsonSerializationException("Cannot convert value that is before Unix epoch of 00:00:00 UTC on 1 January 1970.");
            }
        }
    }
}

Please help me to get this working.

johnslippers
  • 81
  • 1
  • 16
  • In my opinion if the date field changes format you should just make it a string in your model and later on when you are going to use it then convert it. Also your model does not match property DateReveived. – Jonathan Alfaro Mar 22 '20 at 21:47

2 Answers2

0

You can't simply deserialise to an IList, you need an object that has an Items property. For example:

public class RootObject
{
    public IList<Item> Items { get; set; }
}

The other issue I can see is that the JsonProperty for your DateReceived property needs to match the JSON name which is just date.

DavidG
  • 113,891
  • 12
  • 217
  • 223
0

Here is a full working sample:

this is my model:

  public class RootObject
  {
    public List<ItemContainer> Items { get; set; }
  }
  public class ItemContainer
  {
    public Item Item { get; set; }
  }
  public class Item
  {
    public Codes Codes { get; set; }
    public string Date { get; set; }
    public string Name { get; set; }
    public string Price { get; set; }
  }
  public class Codes
  {
    public string Productcode { get; set; }
    public string Departmentcode { get; set; }
    public string Storecode { get; set; }
  }

This is how I deserialize it:

var result = JsonConvert.DeserializeObject<RootObject>(json);//json is a string that contains the json input.

I have noticed several problems with your example:

  1. DataReceived is a property that does not exist in the json model.
  2. Your Json is inconsistent. Name, Date and Price are also found in lowercase: date, name and price. Your json should be consistent.
  3. You are trying to do too much in one spot. You should just take the input of date as a string and later on convert it to one or several useful formats or types. Keep your deserialization/serialization simple and save yourself headaches.
Jonathan Alfaro
  • 4,013
  • 3
  • 29
  • 32