I'm trying to deserialize [{"foo": "1", "bar": false}, {"foo": "2", "bar": false}] into List<(string, bool)> type:
JsonConvert.DeserializeObject<List<(string foo, bool bar)>>(json)
But always get a list of default values - (null, false).
How can I achieve correct deserializing?
P.S. I'm not interested in any model/class for that purpose. I need exactly value tuple instead.
In C#9 you can create a record and use the generated deconstructor to create a ValueTuple. I did see that you did not want to declare a model but it is the closest approach I have found:
Declare the record:
private record FooBar(string foo, bool bar);
Deserialize and deconstruct:
(string foo, bool bar) = JsonConvert.DeserializeObject<FooBar>(json);
or
var (foo, bar) = JsonConvert.DeserializeObject<FooBar>(json);
The C# tuple feature was created to represent sets of values, not entities.
The names of the values are like the names of variables. Like variable names, tuple value names only exist in source code.
(string foo, bool bar) is, actually, just ValueTuple<string, int>. just like (string bar, bool foo):
(string foo, bool bar) a = ('one', true);
(string bar, bool foo) b = a;
The tuple values are stored in fields named Item1, Item2 and so on.
See for yourself how it works here.
If you're that keen into using value tuples for that, you'll have to deserialize yourself:
var json = "[{\"foo\": \"1\", \"bar\": false}, {\"foo\": \"2\", \"bar\": false}]";
var jArray = JsonConvert.DeserializeObject<JArray> (json);
var list = new List<(string foo, bool bar)>();
foreach (var item in jArray)
{
list.Add((item.Value<string>("foo"), item.Value<bool>("bar")));
}
One way to achieve it would be to use a JsonConverter. For example,
public class ValueTupleConverter<U,V> : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(ValueTuple<U,V>) == objectType;
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader,Type objectType,object existingValue,Newtonsoft.Json.JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.Json.JsonToken.Null) return null;
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
var properties = jObject.Properties().ToList();
return new ValueTuple<U, V>(jObject[properties[0].Name].ToObject<U>(), jObject[properties[1].Name].ToObject<V>());
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Now you can use the Converter as following.
var json = "[{'foo': '1', 'bar': false}, {'foo': '2', 'bar': false}]";
var result = JsonConvert.DeserializeObject<IEnumerable<(string,bool)>>(json,new ValueTupleConverter<string,bool>());
foreach(var (foo,bar) in result)
{
Console.WriteLine($"foo:{foo},bar:{bar}");
}
Sample Output
foo:1,bar:False
foo:2,bar:False
I suggest first convert the JSON to model and Deserialize the json
public class item
{
public string foo { get; set; }
public bool bar { get; set; }
}
Method 1 - using foreach
using (StreamReader r = new StreamReader(filepath))
{
string json = r.ReadToEnd();
var obj = JsonConvert.DeserializeObject<List<item>>(json);
Dictionary<string, bool> keyValuePairs = new Dictionary<string, bool>();
foreach (var keyvalue in obj)
{
if (!keyValuePairs.ContainsKey(keyvalue.foo))
keyValuePairs.Add(keyvalue.foo, keyvalue.bar);
}
}
Method 2 - using LINQ without worrying about duplicates
Dictionary<string, bool> keyValuePairs = JsonConvert.DeserializeObject<IEnumerable<item>>(json).ToDictionary(x => x.foo, x => x.bar);
Method 3 - using LINQ by considering duplicates
Dictionary<string, bool> keyValuePairs = JsonConvert
.DeserializeObject<IEnumerable<item>>(json)
.GroupBy(p=>p.foo, StringComparer.OrdinalIgnoreCase)
.ToDictionary(x => x.First().foo, x => x.First().bar);
Method 4 - using DeserializeAnonymousType
var definition = new[] {new { foo = "", bar = false } };
string json = #"[{'foo': '1', 'bar': false}, {'foo': '2', 'bar': true}]";
var obj = JsonConvert.DeserializeAnonymousType(json, definition).Select(p=> (p.foo, p.bar)).ToList();
Related
I'm trying to deserialize some json that looks like this
{
"id":"2021",
"descriptions_bg":[
"30231300",
"30233160",
"32420000",
"30214000"
],
"descriptions_cs":[
"30231300",
"30233160",
"32420000",
"30214000"
],
"descriptions_da":[
"30231300",
"30233160",
"32420000",
"30214000"
],
"official_title_bg":"П",
"official_title_cs":"P",
"official_title_da":"P",
"auth_town_bg":"AuthTown",
"auth_town_cs":"AuthTown",
"auth_town_da":"AuthTown"
}
The problem here being, that there can come an infinite number of items in both descriptions_*, official_title_* and auth_town_* with different endings.
I've tried to make classes and members like public string official_title_* { get; set; }in C# to deserialize to with Newtonsoft Json, but that (of course) doesn't work.
Anyway to fix this?
The best option for you is to deserialize to Dictionary<string, JToken> and based on Key and Object Type you can write business logic. With the combination of Linq you can filter the Key
var json = File.ReadAllText("json1.json");
var dict = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(json);
foreach(var item in dict.Where(x=>x.Key.StartsWith("descriptions_")))
{
Console.WriteLine($"{item.Key} is type {item.Value.GetType()}");
}
foreach (var item in dict.Where(x => x.Key.StartsWith("auth_town_")))
{
Console.WriteLine($"{item.Key} is type {item.Value.GetType()}");
}
An alternative method, using a class object that defines each group of properties that have a common prefix as a Dictionary<string, TValue>.
The JSON is deserialized using a custom JsonConverter that maps each group of properties in the JSON to a Property of the class, using a Dictionary<string, string>:
Dictionary<string, string> map = new Dictionary<string, string>() {
["Descriptions"] = "descriptions_",
["OfficialTitles"] = "official_title_",
["AuthTowns"] = "auth_town_"
};
All properties in the JSON that map to a Property in the class are added to the corresponding Dictionary and the values converted to the Type defined by the class property.
Note: you need to adapt the mapper and class Properties to the JSON. You could make the procedure more generic using some logic to determine when Property Name in the JSON belongs to the same group and generate a new Dictionary<string, TValue> to add to the class by reflection.
Or simply add more Properties to the class off-line, if you find other groups.
Call it as:
(possibly giving the class a less silly name :)
var result = new SimpleSequences().Deserialize(json);
Helper class object:
private class SimpleSequences
{
public SimpleSequences() {
Descriptions = new Dictionary<string, long[]>();
OfficialTitles = new Dictionary<string, string>();
AuthTowns = new Dictionary<string, string>();
}
public List<SimpleSequences> Deserialize(string json)
{
var options = new JsonSerializerSettings() {
Converters = new[] { new SimpleSequencesConverter() }
};
return JsonConvert.DeserializeObject<List<SimpleSequences>>(json, options);
}
public int Id { get; set; }
public Dictionary<string, long[]> Descriptions { get; set; }
public Dictionary<string, string> OfficialTitles { get; set; }
public Dictionary<string, string> AuthTowns { get; set; }
}
Custom JsonConverter:
This converter handles a single object or array of objects (as shown in your question), but always return a List<SimpleSequences> (which may contain a single object). Modify as required.
public class SimpleSequencesConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartArray && reader.TokenType != JsonToken.StartObject) {
throw new Exception($"Unexpected Type. Expecting Array or Object, got {reader.TokenType}");
}
var elementsList = new List<SimpleSequences>();
if (reader.TokenType == JsonToken.StartObject) {
elementsList.Add(GetPropertyValues(JObject.Load(reader)));
}
else {
while (reader.Read() && reader.TokenType != JsonToken.EndArray) {
elementsList.Add(GetPropertyValues(JObject.Load(reader)));
}
}
return elementsList;
}
private SimpleSequences GetPropertyValues(JToken token)
{
var element = new SimpleSequences();
element.Id = token["id"].ToObject<int>();
var descriptions = token.Children().Where(t => t.Path.StartsWith(map["Descriptions"]));
foreach (JProperty p in descriptions) {
element.Descriptions.Add(p.Path, p.Value.ToObject<long[]>());
}
var titles = token.Children().Where(t => t.Path.StartsWith(map["OfficialTitles"]));
foreach (JProperty p in titles) {
element.OfficialTitles.Add(p.Path, p.Value.ToString());
}
var authTowns = token.OfType<JToken>().Where(t => t.Path.StartsWith(map["AuthTowns"]));
foreach (JProperty p in authTowns) {
element.AuthTowns.Add(p.Path, p.Value.ToString());
}
return element;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=> throw new NotImplementedException();
Dictionary<string, string> map = new Dictionary<string, string>() {
["Descriptions"] = "descriptions_",
["OfficialTitles"] = "official_title_",
["AuthTowns"] = "auth_town_"
};
}
Yet another solution could be to use several LINQ2JSON queries.
For instance, if you want to get all description_xyz collections in a single list
then you can do that like this:
var semiParsedJson = JObject.Parse(json);
var descriptionProperties = semiParsedJson.Properties()
.Where(prop => prop.Name.StartsWith("descriptions_"));
var collectionOfDescriptions =
from desc in descriptionProperties.Values()
select ((JArray)desc).ToObject<IEnumerable<string>>();
var flattenedDescriptions = collectionOfDescriptions.
SelectMany(desc => desc)
.ToList();
We can semi parse the json by using JObject.Parse
We can perform some filtering based on the properties' Name field
We can then get all values form each description_xyz collections as IEnumerable<string>
We will have at this point a collection of collections IEnumerable<IEnumerable<string>>, which we can flatten with SelectMany
At very end we can materialize the query with ToList
I get a json string which have few data uniformity issue.
For example one field in json string returns a list of string while the same field in other json string returns a dictionary(key, value pairs).
My class which holds the parsed json values have property for the field as List.
Because of this data uniformity problem, the json string is not parsed properly.
Following is my code to parse the json string
JavaScriptSerializer serializer = new JavaScriptSerializer();
myClass mc = serializer.Deserialize<myClass>(jsonString);
IS there any way with which i can write custom code to parse the json string and map it to myClass?
You don't give a concrete example of what you are trying to accomplish, which means we need to make up an example ourselves. Consider the following class:
public class myClass
{
public Dictionary<string, string> data { get; set; }
}
And consider the following two JSON strings:
{"data": ["zero", 1, "two"]}
{"data": {"0": "zero", "1":1, "2":"two"}}
It seems like you might like to parse these identically, with the array being converted to a Dictionary<string, string> whose keys are array indices. This can be accomplished with the following JavaScriptConverter:
public class myClassConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var myClass = new myClass();
object data;
if (dictionary.TryGetValue("data", out data))
{
if (data.IsJsonArray())
{
myClass.data = data.AsJsonArray()
.Select((o, i) => new KeyValuePair<int, object>(i, o))
.ToDictionary(p => p.Key.ToString(NumberFormatInfo.InvariantInfo), p => serializer.ConvertToType<string>(p.Value));
}
else if (data.IsJsonObject())
{
myClass.data = data.AsJsonObject()
.ToDictionary(p => p.Key, p => serializer.ConvertToType<string>(p.Value));
}
}
return myClass;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new [] { typeof(myClass) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj.IsJsonObject())
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
public static bool IsJsonObject(this object obj)
{
return obj is IDictionary<string, object>;
}
public static IDictionary<string, object> AsJsonObject(this object obj)
{
return obj as IDictionary<string, object>;
}
}
The IDictionary<string, object> passed to Deserialize() corresponds to the key/value pairs in the JSON object being converted. For a particular key ("data" in this case) the object value will be an IDictionary<string, object> if the value is, in turn, a JSON object, and an IEnumerable (specifically an ArrayList) if the value is a JSON array. By testing the value against the appropriate type, a conversion can be made.
The converter only does deserialization. Use it like so:
var jsonString1 = #"{""data"": [""zero"", 1, ""two""]}";
var jsonString2 = #"{""data"": {""0"": ""zero"", ""1"":1, ""2"":""two""}}";
var deserializer = new JavaScriptSerializer();
deserializer.RegisterConverters(new JavaScriptConverter[] { new myClassConverter() });
var newJson1 = new JavaScriptSerializer().Serialize(deserializer.Deserialize<myClass>(jsonString1));
var newJson2 = new JavaScriptSerializer().Serialize(deserializer.Deserialize<myClass>(jsonString2));
Console.WriteLine(newJson1); // Prints {"data":{"0":"zero","1":"1","2":"two"}}
Console.WriteLine(newJson2); // Prints {"data":{"0":"zero","1":"1","2":"two"}}
Debug.Assert(newJson1 == newJson2); // No assert
I have a little test class like so :
public class Command
{
public dynamic MyData { get; set; }
}
As the dynamic MyData I want to use ExpandoObject, so I can do:
Command cmd = new Command();
cmd.MyData = new ExpandoObject();
cmd.MyData.SomeStuff = 4;
cmd.MyData.SomeOtherStuff = "hi";
I am trying to serialize to/deserialize from json. To do this I am using JavaScriptSerializer.
I want an example object above to serialize to:
{
MyData : {
SomeStuff : 4,
SomeOtherStuff : "hi"
}
}
To do this I need a JavaScriptConverter (taken from this website):
public class ExpandoJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return dictionary.ToExpando();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
var dictionary = obj as IDictionary<string, object>;
foreach (var item in dictionary)
result.Add(item.Key, item.Value);
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new Type[] { typeof(ExpandoObject) });
}
}
}
public static class IDictionaryExtensions {
/// <summary>
/// Extension method that turns a dictionary of string and object to an ExpandoObject
/// Snagged from http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/
/// </summary>
public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary) {
var expando = new ExpandoObject();
var expandoDic = (IDictionary<string, object>)expando;
// go through the items in the dictionary and copy over the key value pairs)
foreach (var kvp in dictionary) {
// if the value can also be turned into an ExpandoObject, then do it!
if (kvp.Value is IDictionary<string, object>) {
var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando();
expandoDic.Add(kvp.Key, expandoValue);
}
else if (kvp.Value is ICollection) {
// iterate through the collection and convert any strin-object dictionaries
// along the way into expando objects
var itemList = new List<object>();
foreach (var item in (ICollection)kvp.Value) {
if (item is IDictionary<string, object>) {
var expandoItem = ((IDictionary<string, object>)item).ToExpando();
itemList.Add(expandoItem);
}
else {
itemList.Add(item);
}
}
expandoDic.Add(kvp.Key, itemList);
}
else {
expandoDic.Add(kvp);
}
}
return expando;
}
}
Now this works neat for serializing, but there is a following problem with deserializing:
since MyData is a dynamic object, and the ExpandoJsonConverter expects ExpandoObject, the data deserialized to MyData is of type IDictionary<string, object>.
if I change dynamic MyData to be ExpandoObject MyData, I won't be able to say cmd.MyData.SomeStuff = 4;, the compiler will tell me that "ExpandoObject does not have property named SomeStuff".
finally, I could add dynamic to the list of supported types of ExpandoJsonConverter, byt wait, you cant do typeof(dynamic).
Is anyone aware of a neat workaround? I would really like this functionality, but I can't use 3rd party serialization libraries like Newtonsoft. Thanks.
Deserialize to ExpandoObject but declare the variable dynamic, i.e. what you need is dynamic d = js.Deserialize<ExpandoObject>(json):
string json = #"{
MyData : {
SomeStuff : 4,
SomeOtherStuff : ""hi""
}
}";
var js = new JavaScriptSerializer();
js.RegisterConverters(new[] { new ExpandoJsonConverter() });
dynamic d = js.Deserialize<ExpandoObject>(json);
Console.WriteLine(d.MyData.SomeOtherStuff);
Output:
hi
If you need a Command object, just construct one manually and inject the dynamic object returned from the serializer:
var cmd = new Command { MyData = d };
Console.WriteLine(cmd.MyData.SomeStuff);
I have the following table:
I'd like to execute a LINQ Query that serializes:
JSON:
{
"product-list": {
"products": [
{
"P_Flavor": [
"Berry",
"Cedar",
"Cherry",
"Coffee"
],
"P_Winery": [
"Lyeth"
],
"P_Body": [
"Elegant",
"Firm",
"Firm Tannins",
"Polished",
"Supple",
"Tannins"
],
"P_Name": "A Red Blend",
"P_DateReviewed": "08/31/95",
"P_WineID": 34699,
"P_Score": 5,
}
]
}
}
I would normally use JavaScriptSerializer to do this, however I would like to construct my own JSON payload.
IList<record_property> recList = (from c in entities.record_property
select c).ToList();
var json = new JavaScriptSerializer().Serialize(recList);
What would be the best way to do this?
There may be a quicker/more concise way to do this, but I did it by combining a JavaScriptConverter with a helper type.
The converter (simpler than it looks, inspired from here):
private class RecordPropertyJavaScriptConverter : JavaScriptConverter
{
private static readonly Type[] _supportedTypes = new[]
{
typeof(record_group)
};
public override IEnumerable<Type> SupportedTypes
{
get { return _supportedTypes; }
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (type == typeof(record_group))
{
record_group obj = new record_group();
var kvp = dictionary.Single();
obj.Key = kvp.Key;
obj.Values = serializer.ConvertToType<IEnumerable<object>>(kvp.Value);
return obj;
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var dataObj = obj as record_group;
if (dataObj != null)
{
return new Dictionary<string, object>
{
{dataObj.Key, dataObj.Values}
};
}
return new Dictionary<string, object>();
}
}
The helper type:
private class record_group
{
public string Key;
public IEnumerable<object> Values;
}
The serialization code:
var groups = recList.GroupBy(r => r.Key)
.Select(g => new record_group { Key = g.Key, Values = g.Select(r => r.Value) });
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new [] {new RecordPropertyJavaScriptConverter()});
string json = serializer.Serialize(groups);
The output (with some tabs, newlines added by me):
[{"P_Flavor":["Berry","Cedar","Cherry","Coffee"]},
{"P_Winery":["Lyeth"]},
{"P_Body":["Elegant","Firm","Firm Tannins","Polished","Supple","Tannins"]},
{"P_Name":["A Red Blend"]},
{"P_DateReviewed":["08/31/95"]},
{"P_WineID":[34699]},
{"P_Score":[5]}]
Deserialization can then be done (using the same serializer instance from above) as follows:
var deserialized = serializer.Deserialize<IEnumerable<record_group>>(json);
var properties = deserialized.SelectMany(g => g.Values.Select(v => new record_property { Key = g.Key, Value = v }));
I have the following class:
public class foo
{
public Dictionary<string, string> data = new Dictionary<string, string>();
public foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
}
I need get the value of dictionary using the dot operadotor it's because the class that I set the class as arguments use the dynamic keyword to "walk" on the class.
for example:
var args = new[] {"a","b","c"};
var Foo = new foo(args);
var baa = Foo.data.a;
Console.Write(baa); // ..
if exists an way to make dynamic variables, something like:
public foo(params object[] args) {
foreach (object arg in args) {
var name = (string) arg;
var value = "..";
MakeVariable(name, value);
}
}
makes an variable named of arg and the value .. as public member of foo class.
anyway differents to solve this is very appreciated. Thanks in advance.
You can have Foo inherit from DynamicObject:
public class Foo : DynamicObject
{
private Dictionary<string, string> data = new Dictionary<string, string>();
public Foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (data.ContainsKey(binder.Name))
{
result = data[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
}
To use it you can use dynamic to hold an instance of Foo:
var args= new[] { "a", "b", "c" };
dynamic foo = new Foo(args);
var myA = foo.a; //returns ".."
Keep in mind that you will lose type safety since you have to use dynamic - your use case should really justify this disadvantage - usually there is a better approach.
I think you should use DynamicObject. If you are using an older version of the framework the only option is Reflection.Emit
The dynamic works something like this
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
Another option is to use the ExpandoObject class if you want to directly expose the Data member, as in your example. This keeps the code simpler if you don't need to define specific operations that would require inheriting DynamicObject.
public class Foo
{
public dynamic Data = new ExpandoObject();
public Foo(params object[] args)
{
var dataDict = (IDictionary<string, object>)Data;
foreach (var obj in args)
{
dataDict.Add(obj.ToString(), "..");
}
}
}
Usage:
var foo = new Foo("a", "b", "c");
Console.WriteLine(foo.Data.a);
((IDictionary<string, object>)foo.Data).Add("d", "!");
foreach (var item in foo.Data)
{
Console.WriteLine("{0} : {1}", item.Key, item.Value);
}
Notice that I cast to a dictionary and added "d" although I could've also assigned it directly: foo.Data.d = "!". The only difference is you may not know ahead of time what field names you have, and the former example allows you to setup the ExpandoObject based on dynamic input, whereas the latter is useful when you already know what field name to use.
In .NET 4 this exact behavior is implemented by ExpandoObject class:
public class Foo
{
private readonly ExpandoObject _dict = new ExpandoObject();
public dynamic Data
{
get { return _dict; }
}
public Foo(params object[] args)
{
foreach (var arg in args)
_dict.Add(arg.ToString(), "..");
}
}
var foo = new Foo("a", "b", "c");
foo.Data.x = 3.14;
Console.Write(foo.Data.a);