2

This is the absolute simplest version of this question (a subset of it).

using System;
using System.Collections.Generic;
using System.Text.Json;


public class Program
{
    public class Subscription
    {
        public bool HasRead { get; set; } = true;
        public string TimeStamp { get; set; } = "";
    }



    public static void Main()
    {
        // this input format is a requirement. It cannot be changed.
        string json = @"
          {
            ""305FDF11-25E7-43DE-B09D-DFFC17C79599"": {
              ""hasRead"": true, // EDIT: Removed the double quotes around the boolean. That was not the core issue.
              ""Timestamp"": ""XXX""
            }
          }
        ";

        // I'm trying to find the correct deserialization type in C#
        var deser = JsonSerializer.Deserialize<KeyValuePair<string, Subscription>>(json,
            new JsonSerializerOptions(JsonSerializerDefaults.Web));

        // System.Text.Json.JsonException: 
        //'The JSON value could not be converted to 
        //System.Collections.Generic.KeyValuePair
    }
}

I can't understand why that can't be deserialized.

Note: I'm obsessing over the KeyValuePair type but maybe it has to do with the casing of the fields or something else obvious.

Other note : It still fails when I change KeyValuePair<string, Subscription> to KeyValuePair<string, object> to make it more permissive.

jeancallisti
  • 1,046
  • 1
  • 11
  • 21

5 Answers5

1

First of all, your JSON does not conform to default serialization of KeyValuePair<string, Subscription>, I'd recommend switching the type to Dictionary<string, Subscription>. As well, default JSON deserializer is unable to deserialize your JSON. I'd recommend using Newtonsoft.Json

using Newtonsoft.Json;

string json = @"
          {
            ""305FDF11-25E7-43DE-B09D-DFFC17C79599"": {
              ""hasRead"": ""true"",
              ""Timestamp"": ""XXX""
            }
          }
        ";

var deserialized = JsonConvert.DeserializeObject<Dictionary<string, Subscription>>(json);
var subscription = deserialized["305FDF11-25E7-43DE-B09D-DFFC17C79599"];
Console.WriteLine($"Subscription timestamp is {subscription.TimeStamp}");

Like this you'd get output of Subscription timestamp is XXX.

Giorgi Anakidze
  • 206
  • 1
  • 9
  • 1
    Default serializer was able to deserialize. The core issue is that what I thought was a KeyValuePair was actually a Dictionary. Thanks! – jeancallisti Apr 19 '23 at 13:39
1

You have two issues.

1)

      string json = @"
      {
        ""305FDF11-25E7-43DE-B09D-DFFC17C79599"": {
          ""hasRead"": ""true"",
          ""Timestamp"": ""XXX""
        }
      }
    ";

true is surrounded by quotes. By default that will not deserialise to a boolean but a string. You can change the type of HasRead in the Subscription class to fix the issue. If you want a boolean property then you could add something like:

public bool HasReadBool => bool.Parse(HasRead);

You cannot deserialise directly to a KeyValuePair as that has named Key and Value properties. Instead you can deserialise to a Dictionary:

 var deser = JsonSerializer.Deserialize<Dictionary<string, Subscription>>(json,
        new JsonSerializerOptions(JsonSerializerDefaults.Web));

If you want to get a KeyValuePair you can do this: var kvp = deser.First()

SBFrancies
  • 3,987
  • 2
  • 14
  • 37
  • Your answer is correct but someone answered faster. sorry, they get the accepted answer. PS : See my edit about the boolean – jeancallisti Apr 19 '23 at 12:51
1
using System;
using System.Collections.Generic;
using System.Text.Json;

public class Program
{
    public class Subscription
    {
        public bool HasRead { get; set; } = true;
        public string TimeStamp { get; set; } = "";
    }

    public static void Main()
    {
        // this input format is a requirement. It cannot be changed.
        string json = @"
          {
            ""305FDF11-25E7-43DE-B09D-DFFC17C79599"": {
              ""hasRead"": ""true"",
              ""Timestamp"": ""XXX""
            }
          }
        ";

        // deserialize the JSON string into a dictionary
        var dict = JsonSerializer.Deserialize<Dictionary<string, Subscription>>(json,
            new JsonSerializerOptions(JsonSerializerDefaults.Web));

        // iterate over the dictionary and print each key-value pair
        foreach (var kvp in dict)
        {
            Console.WriteLine($"Key: {kvp.Key}");
            Console.WriteLine($"HasRead: {kvp.Value.HasRead}");
            Console.WriteLine($"TimeStamp: {kvp.Value.TimeStamp}");
        }
    }
}
Serg
  • 3,454
  • 2
  • 13
  • 17
  • Correct answer but someone else answered faster. Sorry, they get the "accepted answer". – jeancallisti Apr 19 '23 at 12:52
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 22 '23 at 00:17
1

We can use type Dictionary<String,Subscription> to achieve it.

using System.Text.Json;

class JsonTest
{
    public class Subscription
    {
        public bool HasRead { get; set; } = true;
        public string TimeStamp { get; set; } = "";
    }
    public static void Main()
    {
        string json = @"
             {
                 ""305FDF11-25E7-43DE-B09D-DFFC17C79599"": {
                      ""hasRead"": ""true"",
                      ""TimeStamp"": ""XXX""
                  }
             }
             ";

        var deser = JsonSerializer.Deserialize<Dictionary<string, Subscription>>(json);
        foreach(var key in deser.Keys)
        {
            Subscription subscription = deser[key];
            Console.WriteLine($"Key:{key} HasRead:{subscription.HasRead} TimeStamp:{subscription.TimeStamp}");
        }
    }
}

I tried to print all values. Output is :-

Key:305FDF11-25E7-43DE-B09D-DFFC17C79599 HasRead:True TimeStamp:XXX

Note :- We should try to match case in json and class properties. Else, We will get null/default values. I have modified Timestamp to TimeStamp in JSON string.

-1

The default serialization of type KeyValuePair<string, Subscription> looks like this:

{
    "key":
      "305FDF11-25E7-43DE-B09D-DFFC17C79599",
    "value":{
     "hasRead":true,
     "timeStamp":"XXX"
    }
}

To change this behavior you need to write your own converter derrived from JsonConverter class. Also note that true value should be without quotes.