I'm trying to parse to a C# object a string containing a JSON that looks like that :
I know it's not really valid json but I can't choose it, it's sent by a device. That's why I've tried to replace the [] by {} to make it look like a valid object.
[2, "2", "text", {Object}]
I've created the following class:
public class MyClass
{
[JsonProperty(Order = 0)]
public int TypeRequest { get; set; }
[JsonProperty(Order = 1)]
public string UniqueID { get; set; }
[JsonProperty(Order = 2)]
public string Action { get; set; }
[JsonProperty(Order = 3)]
public JObject Payload { get; set; }
}
I want to parse the {Object} later (I need to know the "Action" property first because the object depends on the action).
So far I've done :
string userMessage = "[2, "2", "text", {Object}]";
if (userMessage.Length > 2)
{
// We need to remove the first [ and the last ] to be able to parse into a json object
StringBuilder sb = new StringBuilder(userMessage);
sb[0] = '{';
sb[sb.Length - 1] = '}';
userMessage = sb.ToString();
}
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
MyClass objectJSON = jsonSerializer.Deserialize<MyClass >(userMessage);
But it doesn't work I get the following exception:
Invalid object passed in, ':' or '}' expected. (3): {Object}}
I've also tried with JObject.Parse instead and I got:
Invalid JavaScript property identifier character: ,. Path '', line 1,
position 2.
Do you know how to do it? I would like to avoid having to split my JSON by commas and have the cleanest way to do it.
It's not really a valid JSON because of {Object} so I removed it. You can technically do json.Replace("{Object}", "something else") to make it easier. Because you deal with different types in array, it may not be a one step process. Here is an idea for you:
var json = "[2, \"2\", \"text\"]";
var array = JsonConvert.DeserializeObject<JArray>(json);
foreach (var item in array)
{
switch (item.Type)
{
case JTokenType.Integer:
// todo: your parsing code
break;
case JTokenType.String:
break;
// etc.
}
}
I used JSON.NET library to parse JSON. You can install it using nuget:
Install-Package Newtonsoft.Json
If you can, I'd recommend you to fix the JSON source to provide you with a valid JSON that can be parsed to an object without use of low-level classes like JToken, JArray, JObject etc.
Related
Good day everyone, I'm trying to convert a string that looks like this:
"{\"Estado\":true,\"Token\":\"3D16C8D8-058C-4FA7-AEA2-1A764A083B72\",\"Nombre\":\"Agente COV\"}"
If I do a quick inspection when the code is running, it looks like this:
After applying the following line of code:
var Datos = JsonConvert.DeserializeObject<dynamic>(Resultado);
It returns the object with two curly braces
{{"Estado": true, "Token": "3D16C8D8-058C-4FA7-AEA2-1A764A083B72", "Nombre": "Agente COV"}}
How can I avoid those two curly braces after converting the string to dynamic object?
I need to use it like this at the end:
var foo = Datos.Token.Value;
Thank you very much for your help.
The effects you're seeing (escaped quotes in the string and the braces) are just how the debugger has chosen to display those values.
"{\"Estado\":true,\"Token\":\"3D16C8D8-058C-4FA7-AEA2-1A764A083B72\",\"Nombre\":\"Agente COV\"}"
is actually a string that contains
{"Estado":true,"Token":"3D16C8D8-058C-4FA7-AEA2-1A764A083B72","Nombre":"Agente COV"}
and
{{"Estado": true, "Token": "3D16C8D8-058C-4FA7-AEA2-1A764A083B72", "Nombre": "Agente COV"}}
Is how the debugger has chosen to display the dynamic object with 3 properties with values
Estado - true
Token - "3D16C8D8-058C-4FA7-AEA2-1A764A083B72"
Nombre - "Agente COV"
The simplest way I'd tackle this would be to create a class for the JSON
public class MyObject{
public bool Estado { get; set; }
public Guid Token { get; set; }
public string Nombre { get; set; }
}
Then you can use Json.Net to deserialize it.
var json = "{\"Estado\":true,\"Token\":\"3D16C8D8-058C-4FA7-AEA2-1A764A083B72\",\"Nombre\":\"Agente COV\"}";
var myObject = JsonConvert.DeserializeObject<MyObject>(json);
Then access the values like myObject.Token etc.
You can first parse your json with
dynamic Datos = JObject.Parse(yourjson);
and retrieve the value by
var foo = Datos.Token
Note - JObject is from newtonsoft
I'm having trouble reading a JSON list of numbers into a c# int[] array.
I've tried several suggestions from SO, but none have worked.
How would I go about this using JSON.net?
Extract from JSON file:
{
"course": "Norsk",
"grades": [6, 3, 5, 6, 2, 8]
}
What I've tried in c#:
// Reads the JSON file into a single string
string json = File.ReadAllText(jfile);
Console.WriteLine(json);
// Parsing the information to a format json.net can work with
JObject data = JObject.Parse(json);
JToken jToken = data.GetValue("grades");
jGrades = jToken.Values<int>().ToArray();
and:
// Reads the JSON file into a single string
string json = File.ReadAllText(jfile);
Console.WriteLine(json);
// Parsing the information to a format json.net can work with
JObject data = JObject.Parse(json);
for (int o = 0; o < 6; o++) {
var grades = from p in data["Info"[i]] select (int)p["grades"[o]];
jGrades.Add(Convert.ToInt32(grades));
}
As you can see from the c# extracts, I've tried with both arrays and lists, but I can't get it to work.
With the first example (with an array) I get a System.NullRefrenceException, while with the List example, I get several errors, such as Unable to cast object of type 'whereselectlistiterator'2 [Newtonsoft.JSON] to type 'system.iconvertible'
Any help of tips are appreciated.
JObject.Parse(json) is your root object
JObject.Parse(json)["grades"] is the list/array
All you have to do is : converting the items to appropriate type
var list = JObject.Parse(json)["grades"].Select(x => (int)x).ToArray();
You can also declare a class
public class RootObject
{
public string course { get; set; }
public List<int> grades { get; set; }
}
and deserialize whole object as
var myobj = JsonConvert.DeserializeObject<RootObject>(json);
var grade = myobj.grades[0];
I would typically define a class with the relevant properties and simply convert the object.
public class CourseReport
{
public string Course { get; set; }
public ICollection<int> Grades { get; set; }
}
// Reads the JSON file into a single string
string json = File.ReadAllText(jfile);
Console.WriteLine(json);
// Parsing the information to a format json.net can work with
var courseReport = JsonConvert.DeserializeObject<CourseReport>(json);
foreach (var grade in courseReport.Grades)
{
Console.WriteLine(grade);
}
I'm looking for a way to do deserialization from Json to be version dependent using the data within the Json itself.
I'm targeting to use ServiceStack.Text.JsonDeserializer, but can switch to another library.
For example, I'd like to define a data in JSON for v1.0 to be:
{
version: "1.0"
condition: "A < B"
}
and then, a next version of the data (say 2.0) to be:
{
version: "2.0"
condition: ["A < B", "B = C", "B < 1"]
}
At the end, I want to be able to validate version of the data to know how to deserialize the JSON correctly.
UPDATE:
It looks like there is no any kind of implicit support for version-dependent JSON (de)serialization in known products.
The right solution seems to be to split the task by (de)serializing only version part and then use implicit (de)serializing for the correct type(s).
Gratitudes to everyone who shared knowledge and thoughts on the problem.
What you can do is either the following:
Create a base class for the data objects you want to deserialize that contains a version field and nothing else.
Make the data classes for your different versions be derived classes of this base class.
When you deserialize your data object, first, deserialize it as an instance of your base class - so now you have a POCO object that contains the version number. You can use this to decide which of your derived data classes you should use to deserialize your data (in the simplest case, you can do a switch/case and handle each version individually)
An example (using System.Web.Script.Serialization.JavaScriptSerializer):
class BaseClass
{
public int version { get; set; }
}
class FirstVersion: BaseClass
{
public string condition { get; set; }
}
class SecondVersion: BaseClass
{
public IEnumerable<string> condition { get; set; }
}
public void Deserialize (string jsonString)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
BaseClass myData = serializer.Deserialize<BaseClass>(jsonString);
switch (myData.version)
{
case 1:
FirstVersion firstVersion = serializer.Deserialize<FirstVersion>(jsonString);
// ...
break;
case 2:
SecondVersion secondVersion = serializer.Deserialize<SecondVersion>(jsonString);
// ...
break;
}
}
As you can see, this code deserializes the data twice - that may be a problem for you if you are working with large data structures. If you want to avoid that at all costs, you either have to give up static typing or modify the data model of your application.
And here is how it looks like with dynamic:
public void Deserialize (string jsonString)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic myData = serializer.Deserialize<object>(jsonString);
if (myData ["version"] == 1) {
...
}
}
There is also the option to write your own custom JavaScriptConverter. That is a lot more work, but I'm pretty sure you can achieve what you want and it will look nicer.
Another advice to consider is never to remove properties from your JSON structure. If you need to modify a property, keep the old one and add a new one instead - this way, old code can always read data from newer code. Of course, this can get out of hand pretty quickly if you modify your data structures a lot...
In Java, you could use Google's GSON library, as it has a built-in support for versioning. I haven't looked into it, but it is open source and if it's really important to you, I guess you can port the implementation to a different language.
I suggest that you use json.net is it allows you to add your custom type converts which can be used for versioning.
The problem is not serialization as it will always use the current schema. The problem is when the client uses a different type version that the server that receives the object.
What you need to do is to check the version programatically in your type converter and the convert the value by yourself (in this case convert the string to an array).
Documentation: http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
You might want to use the NewtonSoft.Json NuGET package.
This is kind of a standard within the .NET community. It is also often referred to as Json.NET
You can use it like this (example from official website):
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "ExpiryDate": "2008-12-28T00:00:00",
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);
If you are willing to switch to JSON.net, then there is a simpler way of doing it. You don't have to use a BaseClass containing version and you don't have to parse twice. The trick is to use JObject and then query JSON for the version:
JObject obj = JObject.Parse(json);
string version = obj.SelectToken("$.Version")?.ToString();
Then you can proceed as Sándor did with the bonus part that you can use JObject to get your dto instead of re-reading json:
ConditionsDto v1Dto = obj.ToObject<ConditionsDto>(readSerializer);
Putting it all together:
public static ConditionsBusinessObject Parse(string json)
{
JObject obj = JObject.Parse(json);
string version = obj.SelectToken("$.Version")?.ToString();
JsonSerializer readSerializer = JsonSerializer.CreateDefault(/*You might want to place your settings here*/);
switch (version)
{
case null: //let's assume that there are some old files out there with no version at all
//and that these are equivalent to the version 1
case "1":
ConditionsDto v1Dto = obj.ToObject<ConditionsDto>(readSerializer);
if (v1Dto == null) return null; //or throw
List<string> convertedConditions = new List<string> {v1Dto.Condition}; //See what I've done here?
return new ConditionsBusinessObject(convertedConditions);
case "2":
ConditionsDtoV2 v2Dto = obj.ToObject<ConditionsDtoV2>(readSerializer);
return v2Dto == null ? null //or throw
: new ConditionsBusinessObject(v2Dto.Condition);
default:
throw new Exception($"Unsupported version {version}");
}
}
For reference here are the classes that I have:
public class ConditionsDto
{
public string Version { get; set; }
public string Condition { get; set; }
}
public class ConditionsDtoV2
{
public string Version { get; set; }
public List<string> Condition { get; set; }
}
public class ConditionsBusinessObject
{
public ConditionsBusinessObject(List<string> conditions)
{
Conditions = conditions;
}
public List<string> Conditions { get; }
}
and a couple of tests to wrap it up:
[Test]
public void TestV1()
{
string v1 = #"{
Version: ""1"",
Condition: ""A < B""
}";
//JsonHandler is where I placed Parse()
ConditionsBusinessObject fromV1 = JsonHandler.Parse(v1);
Assert.AreEqual(1, fromV1.Conditions.Count);
Assert.AreEqual("A < B", fromV1.Conditions[0]);
}
[Test]
public void TestV2()
{
string v2 = #"{
Version: ""2"",
Condition: [""A < B"", ""B = C"", ""B < 1""]
}";
ConditionsBusinessObject fromV2 = JsonHandler.Parse(v2);
Assert.AreEqual(3, fromV2.Conditions.Count);
Assert.AreEqual("A < B", fromV2.Conditions[0]);
Assert.AreEqual("B = C", fromV2.Conditions[1]);
Assert.AreEqual("B < 1", fromV2.Conditions[2]);
}
In a normal real world application, the //See what I've done here? part is where you will have to do all your conversion chores. I didn't do anything smart there, I just wrapped the single condition to a list to make it compatible with the current business object. As you could guess though, this can explode as the application evolves. This answer in softwareengineering SE has more details in the theory behind versioned JSON data so you might want to have a look in order to know what to expect.
One final word about performance impact of reading to JObject and then converting to the dto is that I haven't done any measurements but I expect it to be better than parsing twice. If I find out that this is not true, I will update the answer accordingly.
Take a look at
System.Web.Script.Serialization.JavaScriptSerializer
Sample
var ser = new JavaScriptSerializer();
var result = (IReadOnlyDictionary<string, object>)ser.DeserializeObject(json);
if(result["version"] == "1.0")
{
// You expect a string for result["condition"]
}
else
{
// You expect an IEnumerable<string> for result["condition"]
}
This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 4 years ago.
We're dealing with an json API result. Just to make our lives difficult the provider of the API returns on of the items as an array if there are multiple objects, or as a single object if there is only one.
e.g.
If there is only one object...
{
propertyA: {
first: "A",
second: "B"
}
}
Or if there are multiple:
{
propertyA: [
{
first: "A",
second: "B"
},
{
first: "A",
second: "B"
}
]
}
Does anybody have a good way of dealing with this scenario?
Ideally we'd like to serialize both to
public class ApiResult{
public ApiItem[] PropertyA {get;set;}
}
This works for the second example but of you encounter the first example you get a A first chance exception of type 'System.MissingMethodException' occurred in System.Web.Extensions.dll
Additional information: No parameterless constructor defined for type of 'ApiItem[]'.
I assume the class definition is as below
public class ApiResult
{
public ApiItem[] PropertyA { get; set; }
}
public class ApiItem
{
public string First { get; set; }
public string Second { get; set; }
}
You can deserialize the json into a dynamic variable, then check the type of d.propertyA. If it's JArray then propertyA is an array, so you can deserialize the json into a ApiResult. If it's JObject then propertyA is a single object, so you need to manually construct ApiItem and assign it to PropertyA of ApiResult. Consider the method below
public ApiResult Deserialize(string json)
{
ApiResult result = new ApiResult();
dynamic d = JsonConvert.DeserializeObject(json);
if (d.propertyA.GetType() == typeof (Newtonsoft.Json.Linq.JObject))
{
// propertyA is a single object, construct ApiItem manually
ApiItem item = new ApiItem();
item.First = d.propertyA.first;
item.Second = d.propertyA.second;
// assign item to result.PropertyA[0]
result.PropertyA = new ApiItem[1];
result.PropertyA[0] = item;
}
else if (d.propertyA.GetType() == typeof (Newtonsoft.Json.Linq.JArray))
{
// propertyA is an array, deserialize json into ApiResult
result = JsonConvert.DeserializeObject<ApiResult>(json);
}
return result;
}
The code above will return an instance of ApiResult for both json examples.
Working demo: https://dotnetfiddle.net/wBQKrp
Building upon ekad's answer, I made the code:
Shorter: as now you don't have map one by one every property inside ApiItem
Probably faster in the case of being already an Array (by not calling to both versions of JsonConvert.DeserializeObject with the same input of the json string)
Explicit about how to introduce other properties
Handling the error case of unknown type of our propertyA (the one that can be either an array or an object).
Notice that instead of JsonConvert.DeserializeObject, I call JObject.Parse, and then ToObject<> for only the part I need in that particular case:
static ApiResult Deserialize(string json)
{
JObject j = JObject.Parse(json);
var propA = j["propertyA"];
switch (propA.Type.ToString())
{
case "Object":
return new ApiResult {
PropertyA = new[]{propA.ToObject<ApiItem>()},
SomethingElse = j["somethingElse"].ToObject<string>(),
};
case "Array":
return j.ToObject<ApiResult>();
default:
throw new Exception("Invalid json with propertyA of type " + propA.Type.ToString());
}
}
The API is pretty much the same, but I've added SomethingElse (for showing how other properties can be easily handled with this approach):
public class ApiResult
{
public ApiItem[] PropertyA { get; set; }
public string SomethingElse { get; set; }
}
public class ApiItem
{
public string First { get; set; }
public string Second { get; set; }
}
Working demo: https://dotnetfiddle.net/VLbTMu
JSON# has a very lightweight tool that allows you to achieve this. It will retrieve embedded JSON, regardless of whether or not the embedded JSON is an object, or array, from within larger JSON objects:
const string schoolMetadata = #"{ "school": {...";
var jsonParser = new JsonObjectParser();
using (var stream =
new MemoryStream(Encoding.UTF8.GetBytes(schoolMetadata))) {
Json.Parse(_jsonParser, stream, "teachers");
}
Here we retrieve a "teachers" object from within a larger "school" object.
best way to Serialize/Deserialize to/from JSON is Json.NET
Popular high-performance JSON framework for .NET
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Sizes = new string[] { "Small" };
string json = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "Expiry": "2008-12-28T00:00:00",
// "Sizes": [
// "Small"
// ]
//}
string json = #"{
'Name': 'Bad Boys',
'ReleaseDate': '1995-4-7T00:00:00',
'Genres': [
'Action',
'Comedy'
]
}";
Movie m = JsonConvert.DeserializeObject<Movie>(json);
string name = m.Name;
// Bad Boys
I am dealing with JSON for the first time and getting data from OpenTSDB. I've created a c# class to deserialize the JSON to but I am getting the error 'Cannot deserialize the current JSON array' as described below.
My c# code to get JSON:
var request = WebRequest.Create("http://localhost:4242/api/query?start=2013/08/21-12:00:00&end=2013/08/22-12:00:00&m=sum:tcollector.collector.lines_sent&o=&yrange=%5B0:%5D&wxh=924x773");
request.ContentType = "application/json; charset=utf-8";
string text;
try
{
var response = (HttpWebResponse) request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
text = sr.ReadToEnd();
}
uxResponse.Text = text;
OpenTSDBResponse myObject = (OpenTSDBResponse)Newtonsoft.Json.JsonConvert.DeserializeObject(text, typeof(OpenTSDBResponse));
var variable = Newtonsoft.Json.JsonConvert.DeserializeObject(text);
//var tester = myObject;
}
catch (Exception ex)
{
uxResponse.Text = GetFullExceptionMessage(ex);
}
The JSON I am receiving from the above code (i.e. the 'text' variable):
[{
"metric":"tcollector.collector.lines_sent",
"tags":
{
"host":"ubuntu1"
},
"aggregateTags":["collector"],
"dps":
{
"1377050434":1271779.0,
"1377050494":1272073.0,
"1377050554":1272502.0,
"1377050614":1273632.0,
"1377050674":1273867.0
}
}]
My c# classes
internal class OpenTSDBResponse
{
[JsonProperty("metric")]
public string Metric { get; set; }
[JsonProperty("tags")]
public Tags Tags { get; set; }
[JsonProperty("aggregateTags")]
public string[] AggregateTags { get; set; }
[JsonProperty("dps")]
public List<TimeValue> TimeValues { get; set; }
}
internal class Tags
{
[JsonProperty("host")]
public string Host { get; set; }
}
internal class TimeValue
{
[JsonProperty("Time")]
public double Time { get; set; }
[JsonProperty("Value")]
public double Value { get; set; }
}
The error when deserializing object:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'MyNamespace.OpenTSDBResponse' because the type requires a JSON
object (e.g. {"name":"value"}) to deserialize correctly.To fix this
error either change the JSON to a JSON object (e.g. {"name":"value"})
or change the deserialized type to an array or a type that implements
a collection interface (e.g. ICollection, IList) like List that can
be deserialized from a JSON array. JsonArrayAttribute can also be
added to the type to force it to deserialize from a JSON array.Path
'', line 1, position 1.
Additional Information
I used the codeproject deserialize JSON project to create my basic classes, but it created a new c# property for each '"1377050434":1271779.0,' so I updated to use my TimeValue class. http://www.codeproject.com/Tips/79435/Deserialize-JSON-with-C
Question:
How do I get this into an appropriate c# class structure?
Additional Information in response to users comments below:
bjaminn's comment:
I believe the JSON you are receiving is an array. The exception is trying to say you are converting an object[] to OpenTSDBResponse when you really want OpenTSDBResponse[]. Another way to debug this would be to look at the variable variable and see what type it is in the debugger. Of course the line that throws the exception would need to be commented out.
Outcome: I modified the deserialize like this
OpenTSDBResponse[] myObject = (OpenTSDBResponse[])Newtonsoft.Json.JsonConvert.DeserializeObject(text, typeof(OpenTSDBResponse[]));
but received the following error when I ran it:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyNamespace.OpenTSDBResponseJsonTypes.TimeValue]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.Path '[0].dps.1377050434', line 1, position 121.
Additional Notes on working solution for other new to JSON
I have added another property to my class for the Dictionary as it's really "unix-time-stamp-data","Value". This allows me to work in c# with datetime/values. There may be a better way for casting but this works an doesn't cause any noticeable performance issues for my scenario.
[JsonProperty("dps")]
public Dictionary<string, double> TimeValues { get; set; }
public List<TimeValue> DataPoints
{
get
{
List<TimeValue> times = new List<TimeValue>();
DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
foreach (var item in TimeValues)
{
times.Add(new TimeValue
{
Time = dtDateTime.AddSeconds(double.Parse(item.Key)).ToLocalTime(),
Value = item.Value
});
}
return times;
}
}
I believe the JSON you are receiving is an array. The exception is trying to say you are converting an object[] to OpenTSDBResponse when you really want OpenTSDBResponse[].
Another way to debug this would be to look at the variable variable and see what type it is in the debugger. Of course the line that throws the exception would need to be commented out.
Tackling new error
It looks like DPS is not a proper JSON array. You could parse it to a dictionary since it looks like the keys will be different in each JSON call.
JSON convert dictionary to a list of key value pairs
New class:
internal class OpenTSDBResponse
{
[JsonProperty("metric")]
public string Metric { get; set; }
[JsonProperty("tags")]
public Tags Tags { get; set; }
[JsonProperty("aggregateTags")]
public string[] AggregateTags { get; set; }
[JsonProperty("dps")]
public Dictionary<string,double> TimeValues { get; set; }
}
You can such so modify your Json Data and your C# Code,for example
[{
"metric":"tcollector.collector.lines_sent",
"tags":
{
"host":"ubuntu1"
},
"aggregateTags":["collector"],
"dps":
[
{"Time":"1377050434","Value":1271779.0},
{"Time":"1377050554","Value":1272502.0}
]
}]
c# Code:
You provide the data is an Array,so when you deserialize the string,you must such so use generic format of deserializeobject
object obj=Newtonsoft.Json.JsonConvert.DeserializeObject<List<OpenTSDBResponse>>(json.ToString());