Alexandr
Alexandr

Reputation: 1901

Deserialize Dictionary<string, T>

For example, I have a some classes:

  class User 
  {
       int Id {get; set;}
       string Name {get; set;}
  }

  class Venue
  {
       int Id {get; set;}
       string Adress {get; set;}
  } 

  class Message
  {
     string Text {get; set;}
     int FromId {get; set;}
  }

I take the json from web:

  [%user% => {id: 1, name: "Alex"}, %user% => {id: 5, name: "John"}]

I can parse it :

var myObjects = JsonConvert.DeserializeObject<Dictionary<string, User>>(json);

But if have a json:

  [%user% => {id: 1, name: "Alex"}, %venue% => {id: 465, adress: "Thomas at 68th Street"}, %message% => {text: "hello", fromId: 78}]

I can define type by key %user% = User, %venue% = Venue, etc..

But how can I parse it?

Thanks in advance!

UPDATE

My current solution:

 private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
        };

 string myJson = "{\"%user%\":{\"id\" : 5, \"name\" : \"John\"}, \"%venue%\":{\"id\" : \"5f56de\", \"adress\": \"Thomas at 68th Street\"}}";

 Dictionary<string, object> dict = 
                JsonConvert.DeserializeObject<Dictionary<string, object>>
                (myJson, _jsonSettings);

 Dictionary<string, object> d = new Dictionary<string, object>();

 foreach(var o in dict)
 {
                string json =  (string)o.Value.ToString();

                switch (o.Key)
                {
                    case "%user%":
                        {
                            var v = JsonConvert.DeserializeObject<User>(json);
                            d.Add(o.Key, v);
                            break;
                        }

                    case "%venue%":
                        {
                            var v = JsonConvert.DeserializeObject<Venue>(json);
                            d.Add(o.Key, v);
                            break;
                        }
                    case "%message%":
                        {
                            var v = JsonConvert.DeserializeObject<Message>(json);
                            d.Add(o.Key, v);
                            break;
                        }
                }
   }

Upvotes: 2

Views: 1773

Answers (2)

YSharp
YSharp

Reputation: 1086

Here's a concise possible solution, using my small JSON parser library:

            public class User
            {
                    public int Id { get; set; }
                    public string Name { get; set; }
            }

            public class Venue
            {
                    public int Id { get; set; }
                    public string Address { get; set; }
            }

            public class Message
            {
                    public string Text { get; set; }
                    public int FromId { get; set; }
            }

            /* Deals with this SO question :
             * 
             * http://stackoverflow.com/questions/19023696/deserialize-dictionarystring-t
             */
            public static void SO_19023696()
            {
                    Console.Clear();
                    Console.WriteLine("StackOverflow question 19023696 - Polymorphic, key-driven Test");
                    Console.WriteLine();
                    string myJson = @"
        [
            {
              ""%user%"" : { ""id"": 1, ""name"": ""Alex""} ,
              ""%venue%"" : { ""id"": 465, ""address"": ""Thomas at 68th Street"" },
              ""%message%"" : { ""text"": ""hello"", ""fromId"": 78 }
            },
            {
              ""%user%"" : { ""id"": 2, ""name"": ""Carl""} ,
              ""%message%"" : { ""text"": ""bye"", ""fromId"": 79 }
            }
        ]";

                    Dictionary<string, object>[] parsed =
                            JSON.Map(null as Dictionary<string, object>[]).
                                    FromJson
                                    (
                                            myJson,
                                            JSON.Map(default(Dictionary<string, object>)).
                                                    Using // Deal with the main issue raised by the SO question:
                                                    (
                                                            (outer, type, value) =>
                                                                    ((outer.Hash != null) && outer.Hash.ContainsKey("Name") ? (Func<object>)
                                                                    (() => new User { Id = (int)outer.Hash["Id"], Name = (string)outer.Hash["Name"] }) :
                                                                            ((outer.Hash != null) && outer.Hash.ContainsKey("Address") ? (Func<object>)
                                                                            (() => new Venue { Id = (int)outer.Hash["Id"], Address = (string)outer.Hash["Address"] }) :
                                                                                    ((outer.Hash != null) && outer.Hash.ContainsKey("Text") ? (Func<object>)
                                                                                    (() => new Message { FromId = (int)outer.Hash["FromId"], Text = (string)outer.Hash["Text"] }) :
                                                                                            null
                                                                                    )
                                                                            )
                                                                    )
                                                    ),
                                            Sample_Revivers.CamelCaseToPascalCase,
                                            Sample_Revivers.DoubleToInteger
                                    );
                    System.Diagnostics.Debug.Assert(parsed[0]["%user%"] is User);
                    System.Diagnostics.Debug.Assert(parsed[0]["%venue%"] is Venue);
                    System.Diagnostics.Debug.Assert(parsed[0]["%message%"] is Message);
                    System.Diagnostics.Debug.Assert(parsed[1]["%user%"] is User);
                    System.Diagnostics.Debug.Assert(parsed[1]["%message%"] is Message);
                    Console.Write("Passed - Press a key...");
                    Console.ReadKey();
            }

More use cases/examples, via generic dictionaries, or anonymous types, or POCOs, can be found here:

https://code.google.com/p/ysharp/source/browse/trunk/TestJSONParser/BasicTests.cs

Upvotes: 0

Shawn Kendrot
Shawn Kendrot

Reputation: 12465

If you are using Json.Net (aka Newtonsoft.Json) you can create a custom JsonConverter object. This object would allows for custom parsing of the json. So, given the following classes

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Venue
{
    public string Id { get; set; }
    public string Address { get; set; }
}

public class Message
{
    public string Text { get; set; }
    [JsonProperty("fromId")]
    public string FromId { get; set; }
}

You could contain those within another class that is assigned a JsonConverter

[JsonConverter(typeof(PostJsonConverter))]
public class Post
{
    public User User { get; set; }
    public Venue Venue { get; set; }
    public Message Message { get; set; }
}

The JsonConvter class is an abstract class with three methods you need to overwrite. You'll want to implement the ReadJson method. If you do not need to write json, then no need to do anything in the WriteJson method.

public class PostJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // not implemented
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // it must be an object being passed in, if not something went wrong!
        if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();

        var postToken = JToken.ReadFrom(reader);
        var userToken = postToken["%user%"];
        var venueToken = postToken["%venue%"];
        var messageToken = postToken["%message%"];

        return new Post
        {
            User = userToken == null ? null : userToken.ToObject<User>(),
            Venue = venueToken == null ? null : venueToken.ToObject<Venue>(),
            Message = messageToken == null ? null : messageToken.ToObject<Message>(),
        };
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

No extra work is needed to convert this from normal conversion because we've given the class the JsonConverterAttribute.

string myJson = "{\"%user%\":{\"id\" : 5, \"name\" : \"John\"}, \"%venue%\":{\"id\" : \"5f56de\", \"address\": \"Thomas at 68th Street\"}}";
Post post = JsonConvert.DeserializeObject<Post>(myJson);

Upvotes: 2

Related Questions