0

First off, I had a hard time with the title of this one; I'll edit it if I get any better suggestions.

I have a json string that looks like this

{
   "chapters": [
      {
         "id": 0,
         "tags": {
            "title": "Chapter 1"
         }
      },
      {
         "id": 1,
         "tags": {
            "title": "Chapter 2"
         }
      }
   ]
}

My model for this is as such

public class Chapter
{
   [JsonPropertyName("id")]
   public int Id { get; set; }

   [JsonPropertyName("tags")]
   public ChapterTags Tags { get; set; }

   [JsonIgnore]
   public string Title
   {
      get { return Tags.Title; }
      set { if (Tags.Title != value) Tags.Title = value; }
   }
}
 
public class ChapterTags
{
   [JsonPropertyName("title")]
   public string Title { get; set; }
}

and here is the code I'm using to deserialize the json

var jsonTask = GetJsonAsync();

using var jsonResults = JsonDocument.Parse(await jsonTask);

var jsonChapters = jsonResults.RootElement.GetProperty("chapters");

List<Chapter> chapters = JsonSerializer.Deserialize<List<Chapter>>(jsonChapters) ?? new();

I want to get rid of the Tags property and the ChapterTags class and just be left with

public class Chapter
{
   [JsonPropertyName("id")]
   public int Id { get; set; }

   public string Title {get; set;}
}

as a simple single class model.

My only thought was to use a JsonConverter but could not figure out how to make that work. I can't change the format of the json input because it is being generated by an outside source.

Also, if it matters, I will never have to re-serialize the object back to json.

I am using VS2022 Preview, as that is currently the only way to work with .Net Maui.

dbc
  • 104,963
  • 20
  • 228
  • 340
master_ruko
  • 629
  • 1
  • 4
  • 22
  • Does this answer your question? [Can I specify a path in an attribute to map a property in my class to a child property in my JSON?](https://stackoverflow.com/questions/33088462/can-i-specify-a-path-in-an-attribute-to-map-a-property-in-my-class-to-a-child-pr) – Charlieface Jul 13 '22 at 21:53
  • That link uses Json.net, not System.Text.Json. I know there are pretty similar, from what I can tell, but it's not a complete answer in this case. – master_ruko Jul 14 '22 at 04:31

2 Answers2

1

try this

List<Chapter> chapters =   JsonDocument.Parse(json)
                           .RootElement.GetProperty("chapters")
                           .EnumerateArray()
                           .Select(c => new Chapter
                           {
                               Id = Convert.ToInt32(c.GetProperty("id").ToString()),
                               Title = c.GetProperty("tags").GetProperty("title").ToString()
                           })
                           .ToList();
Serge
  • 40,935
  • 4
  • 18
  • 45
-1

I ended up going the converter route.

Recap of input JSON

{
   "chapters": [
      {
         "id": 0,
         "tags": {
            "title": "Chapter 1"
         }
      },
      {
         "id": 1,
         "tags": {
            "title": "Chapter 2"
         }
      }
   ]
}

My converter class

public class JsonChapterTitleConverter : JsonConverter<string>
{
   public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
   {
      using var jsonTags = JsonDocument.ParseValue(ref reader);
      var jsonTitle = jsonTags.RootElement.GetProperty("title");

      return jsonTitle.GetString();
   }

   public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => throw new NotImplementedException();
}

New model

public class Chapter
{
   [JsonPropertyName("id")]
   public int Id { get; set; }

   [JsonPropertyName("tags")]
   [JsonConverter(typeof(JsonChapterTitleConverter))]
   public string Title { get; set; }
}

The code used to deserialize the JSON is unchanged

I like the answer Serge gave. In this case however, I feel like it violates the Open-Closed Principle (if I understand it correctly). The JSON shown is not the full string; only what is important to me at the moment. If I decide I need to deserialize more of the elements; not only would I have to extend my model, but also modify the deserialization method in my service class. Using the converter means I only have to add another property to my model corresponding to the JSON property I want to capture.

While Maksym's answer isn't a complete answer, the link provided gave me the boost I needed to figure this out.

Thank you.

Comments welcome.

master_ruko
  • 629
  • 1
  • 4
  • 22