Deserialize Dictionary<string, T> - c#

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;
}
}
}

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);

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

Related

C# Deserializing JSON with Dynamic Keys [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

Deserialize property to different class based on other property c#

I know there are many similar questions on SO, however all the ones I've found require a shared base class in order to work.
With a stream of JSON data like this:
[
{
"webhookType": "order",
"data": {
"id": "eeiefj393",
"orderProperty": "Value"
}
},
{
"webhookType": "customer",
"data": {
"id": 29238,
"customerProperty": "Value"
}
}
]
I wish to deserialize this into two containers, List<Customer> and List<Order>. Where the two classes are as follows:
class Order
{
public string Id { get; set; }
public string OrderProperty { get; set; }
[...]
}
class Customer
{
public long Id { get; set; }
public string CustomerProperty { get; set; }
[...]
}
There may be shared property names however there are no shared properties + type between these two classes and so the solutions involing a sub class aren't working for me.
You need to create a JsonConverter.
DataConverter
public class WebHookConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject item = JObject.Load(reader);
if (item["webhookType"].Value<string>() == "order")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Order>()
};
return webhook;
}
else if (item["webhookType"].Value<string>() == "customer")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Customer>()
};
return webhook;
}
}
return null;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
Objects
[JsonConverter(typeof(WebHookConverter))]
public class WebHook
{
[JsonProperty("webhookType")]
public string Type { get; set; }
public object Data { get; set; }
}
public class Order
{
public string Id { get; set; }
[JsonProperty("orderProperty")]
public string Property { get; set; }
}
public class Customer
{
public long Id { get; set; }
[JsonProperty("customerProperty")]
public string Property { get; set; }
}
Serialization
var json = File.ReadAllText("json1.json");
var obj = JsonConvert.DeserializeObject<List<WebHook>>(json);
var orderList = obj.Where(o => o.Type == "order").Select(o => o.Data).ToList();
var customerList = obj.Where(o => o.Type == "customer").Select(o => o.Data).ToList();
Output:

How to read with interfaces in the model?

I would like to know how to read the JSON with a dynamic layout into my object that has an interface IPeople. When I try I get an error:
'Could not create an instance of type 'xxxx'. Type is an interface or abstract class and cannot be instantiated.
I have spent 2 days searching through Google and I tried using converters and it only works for one object at a time So I would have to choose between a Staff converter and a spectator converter.
How can I get both objects to load using the interface IPeople that loads to the Staff object if the staff data is detected and Spectator object if the spectator data is detected (possibly more object types all have unique properties).
{
"appCore":
{
"Crowd":
[
{
"Name": "Name1",
"Staff":
{
"PopupText":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "IsSeperator":false},
{ "DisplayName":"--------------","PopupValue":"", "IsSeperator":true},
{ "DisplayName":"HT","PopupValue":"HT", "IsSeperator":false}
]
},
"Spectator":
{
"PopupText2":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"--------------","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"Half-Time","PopupValue":"Half-Time", "Cheese":"hiih"}
]
}
}
]
}
}
C# Models:
public class Crowd : ICrowd
{
public IPeople People { get; set; }
}
public class Staff : IPeople
{
IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople
{
IList<PopupTextData2> PopupText2 { get; set; }
}
public class PopupTextData
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}
public class PopupTextData2
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public string Cheese { get; set; }
}
Code used to read the data:
settings.ForEach(type =>
{
builder.Register(c => c.Resolve<MCoreReader>().LoadSection(type))
.As(type.GetInterfaces())
.AsImplementedInterfaces()
.InstancePerRequest()
.InstancePerLifetimeScope();
});
public static object LoadSection(Type type, string _configurationFilePath, string _sectionNameSuffix)
{
if (!File.Exists(_configurationFilePath))
return Activator.CreateInstance(type);
var jsonFile = File.ReadAllText(_configurationFilePath);
var section = ToCamelCase(type.Name.Replace(_sectionNameSuffix, string.Empty));
var settingsData = JsonConvert.DeserializeObject<dynamic>(jsonFile, JsonSerializerSettings);
var settingsSection = settingsData[section];
return settingsSection == null
? Activator.CreateInstance(type)
: JsonConvert.DeserializeObject(JsonConvert.SerializeObject(settingsSection), type, JsonSerializerSettings);
}
private class SettingsReaderContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p =>
{
p.Writable = true;
p.Readable = true;
});
return props;
}
}
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = new SettingsReaderContractResolver(),
};
Assuming you control the Json, here's a working example: I've changed the class structure a bit.
It uses TypeNameHandling.All which writes the type information into the Json - as $type -, so when it's deserialized (using the same setting), it can restore the type.
void Main() {
var crowd = new Crowd {
People = new List<IPeople> {
new Staff {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Staff"}
}},
new Spectator {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Spectator"}
}},
}
};
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
var json = JsonConvert.SerializeObject(crowd, settings);
var newCrowd = JsonConvert.DeserializeObject<ICrowd>(json, settings);
Console.WriteLine(newCrowd.People.Count); // outputs '2'
}
public interface ICrowd {
IList<IPeople> People { get; set; }
}
public interface IPeople {
IList<PopupTextData> PopupText { get; set; }
}
public class Crowd : ICrowd {
public IList<IPeople> People { get; set; }
}
public class Staff : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class PopupTextData {
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}

C# - Deserialize json to dynamic property name [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

Deserializing JSON search results into strongly typed lists (JSON.NET)

I have search results in JSON and I want to deserialize it into strongly typed objects
For example:
{
searchresult: {
resultscount: 15,
results: [
{
resultnumber: 1,
values: [
{
key: "bookid",
value: 1424
},
{
key: "name",
value: "C# in depth"
},
]
}
]
}
}
And I have this POCO
public class Book {
public int BookId { get; set; }
public string Name { get; set; }
}
I want to get a list of books. Yes, I can write my own custom deserializer for this case, but I want to use a default deserializer.
Is it possible to do something like that?
IEnumerable<Book> books = JsonConvert.DeserializeObject<IEnumerable<Book>>(json);
Json.NET will not do this without any customization. You have the following approaches:
Post your classes to http://json2csharp.com/ to create a literal representation of your JSON, then use Linq to transform the result into a list of Book classes:
public class Value
{
public string key { get; set; }
public string value { get; set; } // Type changed from "object" to "string".
}
public class Result
{
public int resultnumber { get; set; }
public List<Value> values { get; set; }
}
public class Searchresult
{
public int resultscount { get; set; }
public List<Result> results { get; set; }
}
public class RootObject
{
public Searchresult searchresult { get; set; }
}
And then
var root = JsonConvert.DeserializeObject<RootObject>(json);
var books = root.searchresult.results.Select(result => new Book { Name = result.values.Find(v => v.key == "name").value, BookId = result.values.Find(v => v.key == "bookid").value });
Create a custom JsonConverter to convert the JSON to your POCO as it is being read, for instance:
internal class BookConverter : JsonConverter
{
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Book);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var values = serializer.Deserialize<List<KeyValuePair<string, string>>>(reader);
if (values == null)
return existingValue;
var book = existingValue as Book;
if (book == null)
book = new Book();
// The following throws an exception on missing keys. You could handle this differently if you prefer.
book.BookId = values.Find(v => v.Key == "bookid").Value;
book.Name = values.Find(v => v.Key == "name").Value;
return book;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class Result
{
public int resultnumber { get; set; }
[JsonProperty("values")]
[JsonConverter(typeof(BookConverter))]
public Book Book { get; set; }
}
public class Searchresult
{
public int resultscount { get; set; }
public List<Result> results { get; set; }
}
public class RootObject
{
public Searchresult searchresult { get; set; }
}
and then
var root = JsonConvert.DeserializeObject<RootObject>(json);
var books = root.searchresult.results.Select(result => result.Book);
Here I only implemented ReadJson as your question only asks about deserialization . You could implement WriteJson similarly if required.
Use Linq to JSON to load the JSON into a structured hierarchy of JObject's then convert the result to Book's with Linq:
var books =
JObject.Parse(json).Descendants()
.OfType<JProperty>()
.Where(p => p.Name == "values")
.Select(p => p.Value.ToObject<List<KeyValuePair<string, string>>>())
.Select(values => new Book { Name = values.Find(v => v.Key == "name").Value, BookId = values.Find(v => v.Key == "bookid").Value })
.ToList();

Categories