2

I am trying to deserialize a JSON object according to my model using the code below:

LoadData<MyModel>(Data.Stats, null);

public void LoadData<TModel>(string data, JsonSerializerSettings jsonSettings) where TModel : class
{
    var mockData = JsonConvert.DeserializeObject<Collection<TModel>>(data, jsonSettings); // ERROR HERE
    Context.SaveChanges();
}

However I am getting an error that reads

Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: {. Path '[0].Statistics', line 7, position 19.'

My JSON object is:

[
  {
    "Id": 3033,
    "Grade": 3,
    "Statistics": { //ERROR OCCURS ON THIS PROPERTY
      "Avatar.Add": 1,
      "TotalPlays": 36,
      "Game.TotalPlays.Spell_Mem_Words": 27,
      "Book.TotalReads.Count": 23,
      "Game.TotalPlays.Count": 39,
      "Character.TotalPlays.L": 23,
      "Character.TotalPlays.E": 3,
      "TotalPlays.Pick_Vocab": 16,
      "Character.TotalPlays.R": 22
    }
  }
]

The Object Model is:

public class MyModel
{
    public int Id { get; set; }
    public int Grade { get; set; }
    public string Statistics { get; set; } 
}

Things I Have Tried

(1) Using json lint I have ensured that the json string is valid.

(2) In javascript serializing the object with back ticks surrounding it works. Backticks don't work in C# JS Fiddle

(3) Tried making the Statistics property in object model to use class called stats instead of string like

public class Stats 
{
    public string Label { get; set;}
    public int Value { get; set; }
}

(4) Tried nearly all the answers on this SO post

Unfortunately I still have not solved this issue. Any ideas?

RyeGuy
  • 4,213
  • 10
  • 33
  • 57
  • 4
    When you used the `Stats` class, how did you declare the `Statistics` property? Have you considered making `Statistics` a `Dictionary`? – Flydog57 Nov 30 '18 at 18:00
  • 2
    Your `Statistics` property is a string. – gunr2171 Nov 30 '18 at 18:00
  • @Flydog57 I will try that. One moment... – RyeGuy Nov 30 '18 at 18:01
  • 1
    Also see: [Accessing properties with a dot in their name](https://stackoverflow.com/questions/36376524/accessing-properties-with-a-dot-in-their-name) in case you do use a class. – gunr2171 Nov 30 '18 at 18:03
  • @Flydog57 I receive the error `The property 'Statistics' is of type 'Dictionary' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]'` – RyeGuy Nov 30 '18 at 18:08
  • 1
    I don't see anything "obviously" wrong. An [MCVE](https://stackoverflow.com/help/mcve) would definitely be helpful. – paulsm4 Nov 30 '18 at 18:28
  • 2
    Possible duplicate of [Deserialize JSON object property to string](https://stackoverflow.com/questions/29980580/deserialize-json-object-property-to-string) – y.luis.rojo Nov 30 '18 at 18:47
  • @paulsm4 how do I make an MCVE for C#? Is there a jsfiddle equivalent for C#? – RyeGuy Nov 30 '18 at 19:14
  • @y.luis I will try to implement the solution in that answer – RyeGuy Nov 30 '18 at 19:16
  • 1
    @RyeGuy - *Is there a jsfiddle equivalent for c#?* -- yes there is see https://dotnetfiddle.net/ – dbc Nov 30 '18 at 19:29
  • 1
    _"Not supported by current database provider"_ What database? I don't see anything database-y in your post. – Flydog57 Nov 30 '18 at 19:34
  • Changing the Statistics property to Dictionary seems to parse the JSON correctly. The error The property 'Statistics' is of type 'Dictionary' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' seems to be an Entity Framework error which is beyond the original question asked. – Dave Nov 30 '18 at 19:35
  • Is the set of possible statistics fixed, or variable? If the set of identifiers is fixed, you could simply create a class with the relevant statistics and mark them with `[JsonProperty("Avatar.Add")]` as shown in [this answer](https://stackoverflow.com/a/24536739/3744182) to [How can I parse a JSON string that would cause illegal C# identifiers?](https://stackoverflow.com/q/24536533/3744182). In fact, one way or another this looks to be a duplicate of that question. – dbc Nov 30 '18 at 20:26
  • Or you could capture the RAW json as shown in [How can I serialize and deserialize a type with a string member that contains “raw” JSON, without escaping the JSON in the process](https://stackoverflow.com/q/40529125/3744182) and [Deserialize JSON object property to string](https://stackoverflow.com/q/29980580/3744182). Do any of those answers work for you? – dbc Nov 30 '18 at 20:28
  • Q: So, do you know how to "make an MCVE for C#"? It's not just for "asking questions on SO". It's a very powerful debugging/troubleshooting technique that's applicable to *ANY* kind of problem. Q: Did it help? Is your problem resolved? – paulsm4 Dec 03 '18 at 17:42

2 Answers2

3

I was able to reproduce the problem with this MCVE:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DeserializeJson
{
    /**
     * REFERENCE:
     * https://stackoverflow.com/questions/53562566/
     *
     * ORIGINAL ERROR:
     * "Unexpected character encountered while parsing value: {. Path '[0].Statistics', line 5, position 19."
     */
     public class Stats
     {
         public string Label { get; set; }
         public int Value { get; set; }
     }

     public class MyModel
     {
         public int Id { get; set; }
         public int Grade { get; set; }
         public string Statistics { get; set; }
     }

    class Program
    {
        static Collection<MyModel> LoadData(string data)
        {
            var retval = JsonConvert.DeserializeObject<Collection<MyModel>>(data);
            return retval;
        }

        static void Main(string[] args)
        {
            try
            {
                string s = File.ReadAllText(@"test-data.json");
                JsonConvert.DefaultSettings = () => new JsonSerializerSettings
                {
                    Formatting = Newtonsoft.Json.Formatting.Indented
                };
                Collection <MyModel> mockData = Program.LoadData(s);
                System.Console.WriteLine("#/items= " + mockData.Count);
                foreach (MyModel item in mockData)
                {
                    System.Console.WriteLine("  id= {0}, Grade={1}, Statistics={2}", item.Id, item.Grade, item.Statistics.ToString());
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("ERROR:", ex);
            }
        }
    }
}

I was able to fix it by:

  1. Elaborating your definition of class Stats, then
  2. Using Stats in the definition of class MyModel:

    public class Stats
    {
        public int AvatarAdd { get; set; }
        public int TotalPlays { get; set; }
        public int GameTotalPlaysSpellMemWords { get; set; }
        public int BookTotalReadsCount { get; set; }
        public int GameTotalPlaysCount { get; set; }
        public int CharacterTotalPlaysL { get; set; }
        public int CharacterTotalPlaysE { get; set; }
        public int TotalPlaysPick_Vocab { get; set; }
        public int CharacterTotalPlaysR { get; set; }
    }
    
    public class MyModel
    {
        public int Id { get; set; }
        public int Grade { get; set; }
        public Stats Statistics { get; set; }
    }
    

You have several choices (including use the above example verbatim). My suggestion would be to break "Statistics" down into smaller model classes.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
0

In My case the issue was the JSON File encoding was chosen as "UTF-8-BOM"

I had to convert that to "UTF-8".

Then it worked fine for me.

enter image description here

Deepak paramesh
  • 495
  • 1
  • 8
  • 19