skip to Main Content

I have a JSON object that looks like this:

{
  "preface": {
    "title": "Preface",
    "content": [
    ],
    "navigation": [
      {
        "text": "Table of Contents",
        "section": "table-of-contents"
      }
    ]
  },
  "table-of-contents": {
    "title": "The Adventures of Sherlock Holmes",
    "content": [
    ],
    "navigation": [
      {
        "text": "A Scandal in Bohemia",
        "section": "a-scandal-in-bohemia"
      },
      {
        "text": "The Red-Headed League",
        "section": "the-red-headed-league"
      },
      {
        "text": "Return to Preface",
        "section": "preface"
      }
    ]
  },
  "a-scandal-in-bohemia": {
    "title": "A Scandal in Bohemia",
    "content": [
      "Chapter I",
      "Chapter II",
      "Chapter III"
    ],
    "navigation": [
      {
        "text": "Chapter I",
        "section": "bohemia-chapter-1"
      },
      {
        "text": "Chapter II",
        "section": "bohemia-chapter-2"
      },
      {
        "text": "Chapter III",
        "section": "bohemia-chapter-3"
      },
      {
        "text": "Return to Table of Contents",
        "section": "table-of-contents"
      }
    ]
  },
  "bohemia-chapter-1": {
    "title": "Chapter I",
    "content": [
      "To Sherlock Holmes she is always the woman. I have seldom heard him mention her under any other name. In his eyes she eclipses and predominates the whole of her sex. It was not that he felt any emotion akin to love for Irene Adler. All emotions, and that one particularly, were abhorrent to his cold, precise but admirably balanced mind. He was, I take it, the most perfect reasoning and observing machine that the world has seen, but as a lover he would have placed himself in a false position. He never spoke of the softer passions, save with a gibe and a sneer. They were admirable things for the observer--excellent for drawing the veil from men's motives and actions. But for the trained reasoner to admit such intrusions into his own delicate and finely adjusted temperament was to introduce a distracting factor which might throw a doubt upon all his mental results. Grit in a sensitive instrument, or a crack in one of his own high-power lenses, would not be more disturbing than a strong emotion in a nature such as his. And yet there was but one woman to him, and that woman was the late Irene Adler, of dubious and questionable memory.",
      "..."
    ],
    "navigation": [
      {
        "text": "Chapter II",
        "section": "bohemia-chapter-2"
      }
    ]
  },
...
}

As you can see, its not an array of objects, and each document has a unique name but the same structure. I would like to know how it is possible to deserialize this into a list of C# objects. Here is what I have so far. My models:

public class Document
{ 
    public Dictionary<string, Section> Section { get; set; }

    public override string ToString() => JsonSerializer.Serialize<Document>(this);
}

public class Section
{
    public string Title { get; set; }
    public List<string> Content { get; set; }

    [JsonPropertyName("navigation")]
    public List<Navigation> Navigations { get; set; }

    public override string ToString() => JsonSerializer.Serialize<Section>(this);
}

The above is how I am serializing the json text file. I would like to deserialize it into a list of objects to display on a Razor view. Here is my service method:

public IEnumerable<Document> GetDocuments()
{
    using (var jsonFileReader = File.OpenText(JsonFileName))
    {
        return JsonSerializer.Deserialize<List<Document>>(jsonFileReader.ReadToEnd(),
            new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });
    } 
}

My Index.html code behind:

public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;
    public JsonFileService DocumentService;
    public IEnumerable<Document> Documents { get; private set; }

    public IndexModel(ILogger<IndexModel> logger, JsonFileService documentService)
    {
        _logger = logger;
        DocumentService = documentService;
    }

    public void OnGet()
    {
        Documents = DocumentService.GetDocuments();
    }
}

And finally in my Razor view I just have this foreach loop:

foreach (var document in Model.Documents)
{
    <h3>@document.Section.Title</h3>
}

When I run the program, I get the following error and stacktrace:

JsonException: The JSON value could not be converted to System.Collections.Generic.List`1[WebApp.Models.Document]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
System.Text.Json.Serialization.JsonCollectionConverter<TCollection, TElement>.OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TCollection value)
System.Text.Json.Serialization.JsonConverter<T>.TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T value)
System.Text.Json.Serialization.JsonConverter<T>.ReadCore(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state)
System.Text.Json.JsonSerializer.ReadFromSpan<TValue>(ReadOnlySpan<byte> utf8Json, JsonTypeInfo jsonTypeInfo, Nullable<int> actualByteCount)
System.Text.Json.JsonSerializer.ReadFromSpan<TValue>(ReadOnlySpan<char> json, JsonTypeInfo jsonTypeInfo)
System.Text.Json.JsonSerializer.Deserialize<TValue>(string json, JsonSerializerOptions options)
WebApp.Services.JsonFileService.GetDocuments() in JsonFileService.cs
+    return JsonSerializer.Deserialize<List<Document>>(jsonFileReader.ReadToEnd(),
WebApp.Pages.IndexModel.OnGet() in Index.cshtml.cs
+    Documents = DocumentService.GetDocuments();

I feel like I am missing a very simple step here. It seems like because the JSON object is not an array of objects, the deserializer cannot convert it to an IEnumerable. Could someone please guide me on how to convert this JSON into a list of C# objects?

2

Answers


  1. It seems like because the JSON object is not an array of objects, the deserializer cannot convert it to an IEnumerable

    Yes, it is not an array of objects, it is an object with others objects in it, I believe that using default serializers/deserializers you will not be able to work this JSON out.

    Unless you end up using dynamic objects (can be a JObject) and then cast it to another structure, the only way is to write a custom serializer for this specific case and build an workaround this JSON.

    You can try this:

    var obj = JsonConvert.DeserializeObject<JObject>(json);
    var childrenObjects = obj.Children();
    

    This will give you a list of your four elements on this JSON, then you can cast it to an appropriate object and work with it.

    Login or Signup to reply.
  2. Your JSON is in the shape of the value you defined for Section

    public Dictionary<string, Section> Section { get; set; }
    

    ie, it is a Dictionary<string,Section>. You cant deserialize it into a List of these. You must deserialize it to that type

    public Dictionary<string,Section> GetDocuments()
    {
        using (var jsonFileReader = File.OpenText(JsonFileName))
        {
            return JsonSerializer.Deserialize<Dictionary<string,Section>>(jsonFileReader.ReadToEnd(),
                new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });
        } 
    }
    

    Live example: https://dotnetfiddle.net/iUGsR7

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search