I'm looking way to Deserialize JSON string to c# List<Tuple<string, string>>.
"[{\"name\":\"OkeyTablePaired\",\"value\":\"true\"},
{\"name\":\"OkeyTableIndicator\",\"value\":\"true\"},
{\"name\":\"OkeyTableHued\",\"value\":\"true\"},
{\"name\":\"OkeyTableSpectatorQuiet\",\"value\":\"true\"},
{\"name\":\"OkeyTableEveryoneQuiet\",\"value\":\"true\"}]"
Tuple List:
List<Tuple<string, string>> tupleJson = new List<Tuple<string, string>>();
I would like to put them together as
[OkeyTablePaired]:[true]
[OkeyTableIndicator]:[false]
[OkeyTableHued]:[true]
[OkeyTableSpectatorQuiet]:[true]
[OkeyTableEveryoneQuiet]:[true]
in the List Tuple...
Any help would be fantastic.
Thanks.
This should work. Note that you need to convert the input to valid json array by adding brackets [] first. You will need to get JSON.NET to make this work.
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using Newtonsoft.Json.Linq;
string validJson = "[" + json + "]";
JArray jsonArray = JArray.Parse(validJson);
List<Tuple<string, string>> tupleJson = jsonArray
.Select(p => new Tuple<string, string>((string)p["name"], (string)p["value"]))
.ToList();
More info in the documentation.
Assuming you get a valid JSON array, a custom converter with JSON.NET would work as well:
public class TupleConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<Tuple<string, string>>);
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
List<Tuple<string, string>> result = null;
if (reader.TokenType == JsonToken.StartArray)
{
JArray deserialized = JArray.Load(reader);
result = new List<Tuple<string, string>>(deserialized.Count);
foreach (var token in deserialized)
{
if (token.Type == JTokenType.Object)
{
result.Add(Tuple.Create(
token["name"].ToObject<string>(),
token["value"].ToObject<string>()));
}
}
}
return result;
}
}
Usage:
List<Tuple<string, string>> result =
JsonConvert.DeserializeObject<List<Tuple<string, string>>>(json, new TupleConverter());
Example: https://dotnetfiddle.net/TEbNsH
If you've created a data contract for Tuple that the JSON can interpret, you can use DataContractJsonSerializer (from the System.Runtime.Serialization.Json library):
var request = WebRequest.Create(requestUrl) as HttpWebRequest;
request.Method = "GET";
var jsonSerializer = new DataContractJsonSerializer(typeof (Tuple));
var objResponse = (Tuple) jsonSerializer.ReadObject(response.GetResponseStream());
The data contract, in your case, would probably be pretty straightforward, something like this:
[DataContract]
public class Tuple
{
[DataMember]
public string OkeyTablePaired {get; set;}
[DataMember]
public string OkeyTableIndicator {get; set;}
.....etc.
}
Related
I thought this would pretty easily to convert from JSON array to a comma-separated string and back using Newtonsoft, but I am having issues getting the ReadJson to work. I thought I would just deserialize from the reader to a string array and then call Join, but I keep getting errors: Unexpected token while deserializing object: PropertyName. Path '[0]..
Here is the code I am using:
public class myModel
{
[JsonConverter(typeof(CommaSeperatedStringJsonConverter))]
public string myString { get; set; }
public int myNumber { get; set; }
}
public class CommaSeperatedStringJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var stringArray = serializer.Deserialize(reader, typeof(string[]));
return string.Join(",", stringArray);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is null)
{
writer.WriteNull();
}
else
{
serializer.Serialize(writer, value.ToString().Split(','), typeof(string[]));
}
}
}
Try using the generic version of Deserialize instead. In other words, change this line:
var stringArray = serializer.Deserialize(reader, typeof(string[]));
To this:
var stringArray = serializer.Deserialize<string[]>(reader);
Fiddle: https://dotnetfiddle.net/KpQSiG
It shouldn't matter, but for some reason it does in this situation. If I have more time I'll try to dig into the source code to see what is happening.
I'm trying to write a generic method that will take a JSON string and convert it to a DataTable (the best it can).
It works fine until it comes across a JSON object that has an array as one of the properties.
What I would like to do, is ignore the properties that are arrays and convert as best it can.
I have an example fiddle that explains what I am trying to do:
https://dotnetfiddle.net/cgkNov
public static void Main()
{
const string JsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",""Name"":""1"",""MyArray"":[]}]";
const string EndResultJsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",""Name"":""1""}]";
var settings = new JsonSerializerSettings
{
Error = (obj, args) =>
{
var context = args.ErrorContext;
context.Handled = true;
}
};
var a = JsonConvert.DeserializeObject<DataTable>(JsonObject, settings);
Console.WriteLine(JsonConvert.SerializeObject(a));
a = JsonConvert.DeserializeObject<DataTable>(EndResultJsonObject, settings);
Console.WriteLine(JsonConvert.SerializeObject(a));
}
Thanks,
Alex
If you want to skip array, you can use something like this. It is just an example, and probably you should improve it.
Add custom converter:
public class SkipArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return !objectType.IsArray;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
DataTable dataTable = new DataTable();
int i = 0;
foreach (JToken token in array.Children())
{
var dataRow = dataTable.NewRow();
if (token.Type != JTokenType.Object) continue;
if (i == 0)
{
SetColumns(dataTable, token);
dataRow = dataTable.NewRow();
i++;
}
foreach (JToken jToken in token)
{
if (((JProperty)jToken).Value.Type != JTokenType.Array)
{
string name = ((JProperty)jToken).Name;
object value = ((JValue)((JProperty)jToken).Value).Value;
dataRow[name] = value;
}
}
dataTable.Rows.Add(dataRow);
}
return dataTable;
}
private void SetColumns(DataTable dt, JToken token)
{
foreach (JToken jToken in token)
{
if (((JProperty)jToken).Value.Type != JTokenType.Array)
{
string name = ((JProperty)jToken).Name;
object value = ((JValue)((JProperty)jToken).Value).Value;
Type valueType = value.GetType();
dt.Columns.Add(name, valueType);
}
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
After this you can use it:
JsonSerializerSettings settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new SkipArrayConverter() }
};
const string JsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",Name:""1"",MyArray:[{}]}]";
const string EndResultJsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",""Name"":""1""}]";
var a = JsonConvert.DeserializeObject<DataTable>(JsonObject, settings);
Console.WriteLine(JsonConvert.SerializeObject(a));
a = JsonConvert.DeserializeObject<DataTable>(EndResultJsonObject, settings);
Console.WriteLine(JsonConvert.SerializeObject(a));
I noticed how you are using double " to wrap fields' keys and values. In your current sample code for JsonObject you are not wrapping the empty array [] in quotes. I tried changing it to
const string JsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",""Name"":""1"",""MyArray"":""[]""}]";
That solves the issue with your current data atleast. Since you're using a Third party library to do the actual parsing of JSON, you have limited control over the process.
Maybe what you can try is creating a minified version of DataTable class and then use this new version to parse the JSON string into an object of the new MiniDT class. Let me know if you need some help with that.
Cheers.
use this
const string JsonObject = #"[{Id:""672af604-495b-4dc0-933e-6c55f56bef82"",""Name"":""1"",""MyArray"":[{}]}]";
Array should be declared like this
MyArray:[{}]
Alex,
I found this answer made by Brian Rogers: Removing empty array members from a JSON string
I think this is what you where searching for. Try it out and if it isn't then we can create a code using it as an inspiration.
I have my serialized JSON in this format:
string json = #"[{"Name": "std_id","Value": "111"}, {"Name": "cust_id","Value": "444"}]"
How do I deserialize it to a single anonymous object like this:
var paramObj = new {"std_id" = 111, "cust_id" = 444}
Since you said the values of the Name and Value properties in your JSON objects can vary, you will not be able to deserialize to an anonymous object. Anonymous types are defined at compile-time, which means you need to know the property names ahead of time to be able to define them. The only way to get around that is code generation, which I think is going to be overkill for this situation. Instead, I would suggest you deserialize into a JObject with a dynamic variable. This will get you pretty close to what you want. Here's how:
string json = #"[
{ ""Name"": ""std_id"", ""Value"": ""111"" },
{ ""Name"": ""cust_id"", ""Value"": ""444"" }
]";
dynamic obj = new JObject(JArray.Parse(json)
.Select(t => new JProperty((string)t["Name"], t["Value"])));
From there, you can access the properties like you would for an anonymous type (assuming you know what they are):
Console.WriteLine(obj.std_id);
Console.WriteLine(obj.cust_id);
If you don't know what the properties are, you can enumerate them like a dictionary:
foreach (var prop in obj)
{
Console.WriteLine(prop.Name + ": " + prop.Value);
}
Fiddle: https://dotnetfiddle.net/MRY2ny
Why anonymous object? you should deserialize to a type like below
public class RootObject
{
public string Name { get; set; }
public string Value { get; set; }
}
Then what you actually have is IEnumerable<RootObjct>. You can use use Linq and select First() from it like
RootObject = RootObjects.FirstOrDefault()
You could deserialize it into a dynamic. Like this:
var serializer = new JavaScriptSerializer();
var deserializedResult = serializer.Deserialize<dynamic>(json);
Reference:
JavaScriptSerializer Class
var definition = new { Name = "" };
string json1 = #"{'Name':'James'}";
var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
Console.WriteLine(customer1.Name);
string json2 = #"{'Name':'Mike'}";
var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
Console.WriteLine(customer2.Name);
source http://www.newtonsoft.com/json/help/html/DeserializeAnonymousType.htm
I know that this solution isn't perfect, but it works for the example provided by you and returns result that looks like paramObj in your example.
The idea is to create a custom Json converter.
First, let's create a DTO class to present a name-value item of incomming JSON.
public class NameValueJsonItem
{
public string Name { get; set; }
public string Value { get; set; }
}
Converter implementation:
public class DynamicJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token == null || token.Type != JTokenType.Array)
{
return null;
}
List<NameValueJsonItem> parsedJson = token.ToObject<List<NameValueJsonItem>>();
ExpandoObject result = new ExpandoObject();
foreach (NameValueJsonItem item in parsedJson)
{
if (!String.IsNullOrEmpty(item.Name))
{
(result as IDictionary<string, object>)[item.Name] = item.Value;
}
}
return result;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Sure, you can make it more safe by adding some exceptions handling etc inside the method if you want.
You can use this converter like that:
dynamic result = JsonConvert.DeserializeObject<dynamic>(json, new DynamicJsonConverter());
Hope it will help.
Using Json.net, deserializing a type that contains a Tuple<...> doesn't work (serialization works, but deserialization doesn't):
[TestMethod]
public void Test()
{
var orig = new TupleHolder("what????", true);
var json = JsonConvert.SerializeObject(orig);
Assert.AreEqual("{\"Tup\":{\"Item1\":\"what????\",\"Item2\":true}}", json);
// great! serialization works like a charm! now let's test deserialization:
var dupl = JsonConvert.DeserializeObject<TupleHolder>(json);
Assert.AreEqual("ZZZ", dupl.Tup.Item1); // pass! but it should be "what????"... what????
Assert.AreEqual(false, dupl.Tup.Item2); // pass! but it should be "true", right???
Assert.AreEqual(orig.Tup.Item1, dupl.Tup.Item1); // fail!
Assert.AreEqual(orig.Tup.Item2, dupl.Tup.Item2); // fail!
}
public class TupleHolder
{
public Tuple<string, bool> Tup { get; set; }
public TupleHolder() { Tup = new Tuple<string, bool>("ZZZ", false); }
public TupleHolder(string s, bool b) { Tup = new Tuple<string, bool>(s, b); }
}
Funny thing is that direct deserialization of Tuple<...> does work:
[TestMethod]
public void Test2()
{
var orig = new Tuple<string, bool>("ABC", true);
var json = JsonConvert.SerializeObject(orig);
var dupl = JsonConvert.DeserializeObject<Tuple<string, bool>>(json);
Assert.AreEqual(orig, dupl); // direct deserialization of Tuple<...> works.
}
Is it a Json.NET bug or am I missing here something?
The answer provided by Remi helped me. I took his TupleConverter and made it generic for a 2-tuple. The concept is the same for any N-tuple.
I leave it here in case it helps someone.
public class TupleConverter<U, V> : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Tuple<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 target = new Tuple<U, V>(
jObject["m_Item1"].ToObject<U>(), jObject["m_Item2"].ToObject<V>());
return target;
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Note: My Tuple was JSON serialized with m_Item1 and m_Item2, so I had to change jObject["ItemX"] to jObject["m_ItemX"]
Usage example with a List<Tuple<int, User>>:
string result = "String to deserialize";
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new TupleConverter<int, User>());
List<Tuple<int, User>> users = JsonConvert.DeserializeObject<List<Tuple<int, User>>>(result, settings);
The solution - or mine, anyhow - is to define a custom converter for the Tuple.
This example provides a concrete solution for a specific Tuple, but you could genericize it to make the TupleConverter class to handle any combination of value types. Could also make it abstract and have derived types implement instantiation methods for each item, to handle tuples with reference types.
public class TupleConverter : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Tuple<string, bool>) == 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 target = new Tuple<string, bool>(
(string)jObject["Item1"], (bool)jObject["Item2"]);
return target;
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
public class TupleHolder
{
[Newtonsoft.Json.JsonConverter(typeof(TupleConverter))]
public Tuple<string, bool> Tup { get; set; }
public TupleHolder() { Tup = new Tuple<string, bool>("ZZZ", false); }
public TupleHolder(string s, bool b) { Tup = new Tuple<string, bool>(s, b); }
}
[Test]
public void Test()
{
var orig = new TupleHolder("what????", true);
var json = Newtonsoft.Json.JsonConvert.SerializeObject(orig);
Assert.AreEqual("{\"Tup\":{\"Item1\":\"what????\",\"Item2\":true}}", json);
var dupl = Newtonsoft.Json.JsonConvert.DeserializeObject<TupleHolder>(json);
// These succeed, now
Assert.AreEqual(orig.Tup.Item1, dupl.Tup.Item1);
Assert.AreEqual(orig.Tup.Item2, dupl.Tup.Item2);
}
I ended up with something more generic, hope it helps
public class TupleConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var match = Regex.Match(objectType.Name, "Tuple`([0-9])", RegexOptions.IgnoreCase);
return match.Success;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
try
{
var tupleTypes = objectType.GetProperties().ToList().Select(p => p.PropertyType).ToArray();
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
var valueItems = new List<object>();
for (var i = 1; i <= tupleTypes.Length; i++)
valueItems.Add(jObject[$"m_Item{i}"].ToObject(tupleTypes[i - 1]));
var convertedObject = objectType.GetConstructor(tupleTypes)?.Invoke(valueItems.ToArray());
return convertedObject;
}
catch (Exception ex)
{
throw new Exception("Something went wrong in this implementation", ex);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
I used a Dictionary in a Web API project, which is serializing like that in JSON:
{"keyname":{objectvalue},"keyname2:"....
Since I have duplicate keys I could't use Dictionary type any more, and instead now I'm using List<KeyValuePair<string,object>>.
But this is serializing that way:
[{"Key":"keyname","Value":"objectvalue"}...
Is there a way to have the List<KeyValuePair> serialize the same way a dictionary does?
Thanks.
If you use the Newtonsoft Json.NET library you can do the following.
Define a converter to write the list of key/value pairs the way you want:
class MyConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<KeyValuePair<string, object>> list = value as List<KeyValuePair<string, object>>;
writer.WriteStartArray();
foreach (var item in list)
{
writer.WriteStartObject();
writer.WritePropertyName(item.Key);
writer.WriteValue(item.Value);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// TODO...
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<KeyValuePair<string, object>>);
}
}
Then use the converter:
var keyValuePairs = new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("one", 1),
new KeyValuePair<string, object>("two", 2),
new KeyValuePair<string, object>("three", 3)
};
JsonSerializerSettings settings = new JsonSerializerSettings { Converters = new [] {new MyConverter()} };
string json = JsonConvert.SerializeObject(keyValuePairs, settings);
This generates [{"one":1},{"two":2},{"three":3}]