deserialize json in C# with illegal variable characters - c#

I'm writing a .NET library (using Newtonsoft) that interacts with a REST service, and I have a service that's returning json, but the json id field is called '$id'. So I can't just create a corresponding $id property in my C# data class. I tried to use something like
[JsonObject(MemberSerialization.OptOut)]
public class DocData
{
[JsonProperty("$id")]
public string id { get; set; }
public string Name { get; set; }
}
but while Name gets assigned, the id property does not. Anyone know how to map this json key to .NET?
Thanks

It looks like this is a bug in JSON.NET, and I think you should report it. It works fine in LINQPad for any property name except $id.
void Main()
{
var s = JsonConvert.SerializeObject(new DocData{id = "hi", Name = "world"}).Dump();
JsonConvert.DeserializeObject<DocData>(s).Dump();
}
public class DocData
{
// [JsonProperty("i$d")] // this would work
// [JsonProperty("_id")] // as would this
// [JsonProperty("$name")] // and even this
[JsonProperty("$id")] // but this fails
public string id { get; set; }
public string Name { get; set; }
}
Evidently Json.NET uses $id as a reserved word to help it deal with object references.
var dd = new DocData{id = "hi", Name = "world"};
JsonConvert.SerializeObject(new[]{dd, dd}, new JsonSerializerSettings{PreserveReferencesHandling = PreserveReferencesHandling.Objects}).Dump();
// Output: [{"$id":"1","$id":"hi","Name":"world"},{"$ref":"1"}]
Still, it seems like it should let you capture the $id property if you're not using reference handling.
As a workaround, you can parse it into a JObject, and pull the property out directly:
var id = JObject.Parse(s)["$id"];
var obj = JsonConvert.DeserializeObject<DocData>(s);
obj.id = id;

Related

C# dynamic class - how to preserved the var name from serialize output

The variable name with # prefix changed after serialize due C# naming. How to prevent this?
//assignment of object value with #Timestamp
List<dynamic> Documents = new List<dynamic>();
Documents.Add( new { Index = ""index-name-test", Type = "doc", Id = g.ToString(),
Title = "title1", #Timestamp = DateTime.UtcNow });
foreach (var doc in Documents)
{
var json = JsonConvert.SerializeObject(new { Documents= doc });
}
as in the json value, it contain
"{\"Documents\":{\"Index\":\"index-name-test*\",\"Type\":\"doc\",\"Id\":\"76134434-2ed0-48df-9034-841b386a0dbc\",\"Title\":\"title1\",\"Timestamp\":\"2019-04-14T15:50:33.596931Z\"}}"
{"Documents":{"Index":"index-name-test*","Type":"doc","Id":"76134434-2ed0-48df-9034-841b386a0dbc","Title":"title1","Timestamp":"2019-04-14T15:50:33.596931Z"}}
How to make Timestamp become #Timestamp ?
In C#, prefixing a variable name with # is just a way to allow you to use reserved words as variable names. For example, you can do this:
var #class = "foo";
If you don't use a # you will get a compiler error. As such, when you serialise your dynamic objects, the variable name is still Timestamp. The best option would be to use a concrete class to store your data. Not only can you then control the names when serialising, but you get compile time type safety and it's far quicker than using dynamic (every time you use dynamic a kitten dies!)
So I would create two classes like this:
//Root class so you don't need to serialise an anonymous type and can easily deserialise later
public class Root
{
public List<Document> Documents { get; set; }
}
public class Document
{
public string Index { get; set; }
public string Type { get; set; }
public string Id { get; set; }
public string Title { get; set; }
//This attribute controls the JSON property name
[JsonProperty("#Timestamp")]
public string Timestamp { get; set; }
}
And serialise something like this:
var root = new Documents();
root.Documents = new List<Document>
{
new Document
{
Index = ""index-name-test",
Type = "doc",
Id = g.ToString(),
Title = "title1",
Timestamp = DateTime.UtcNow
}
};
var json = JsonConvert.SerializeObject(root);

How do I parse JSON data which contains only arrays and nested arrays with no property names?

So I've looked around for tutorials on this and all the tutorials I've found doesn't have JSON that looks like the one I'm trying to parse.
I'm trying to parse JSON from this website https://www.steamcardexchange.net/api/request.php?GetBadgePrices_Guest
Since it doesn't have any identifiers for each thing like name, id etc I'm not sure how I will go about extracting data from it.
My only interest is really getting the first number of each item
[["449940","! That Bastard Is Trying To Steal Our Gold !"],5,"$0.64","1519294200"]
so what I want to extract from this item would be "449940".
This is what I've got so far
using (var client = new WebClient())
{
client.DownloadFile("https://www.steamcardexchange.net/api/request.php?GetBadgePrices_Guest", "data.json");
}
using (StreamReader r = new StreamReader("data.json"))
{
string json = r.ReadToEnd();
//Parse somehow
}
Any tips?
I took this up out of sheer curiosity because I had no idea how to parse this either. Perhaps there's a much better way.
I started by pasting a fragment of this into json2csharp.com.
The class it generates is
public class RootObject
{
public List<List<object>> data { get; set; }
}
From there I wrote some classes that correspond to what I think the data is supposed to look like. The names of the classes and properties are meaningless, so change them to whatever these actually represent.
public class OutputItem
{
public Message Message { get; set; }
public long Int64Value { get; set; } // 5
public string StringThatLooksLikeCurrency { get; set; } // "$0.64"
public string StringThatLooksNumeric { get; set; } // "1519294200"
}
public class Message
{
public string MessageId { get; set; } // "449940"
public string MessageText { get; set; } // "! That Dude..."
}
And finally, some sample code that takes a fragment of that JSON and converts it to a list of OutputItem. In order to figure this out I first deserialized the JSON to RootObject, then I inspected the deserialized object in the debugger to make sense of what it looked like.
var json = #"{ ""data"": [[[ ""449940"", ""! That Dude Is Trying To Steal Our Gold !"" ], 5, ""$0.64"", ""1519294200"" ], [[ ""303720"", ""#killallzombies"" ], 5, ""$0.56"", ""1519322799"" ]]}";
var parsed = JsonConvert.DeserializeObject<RootObject>(json);
var outputItems = new List<OutputItem>();
foreach (var listOfObject in parsed.data)
{
var outputItem = new OutputItem();
var message = (JArray) listOfObject[0];
outputItem.Message = new Message {MessageId = (string) message[0],
MessageText = (string) message[1]};
outputItem.Int64Value = (long) listOfObject[1];
outputItem.StringThatLooksLikeCurrency = (string) listOfObject[2];
outputItem.StringThatLooksNumeric = (string) listOfObject[3];
outputItems.Add(outputItem);
}

JSON Object to properties

I want to store the results from JSON in properties, but I don't know how. I'm using ASP.NET with MVC 4/Razor.
My model's constructor is:
public UserModel()
{
WebClient request = new WebClient();
string response = request.DownloadString(url);
JObject _naturalUserObject = (JObject)JsonConvert.DeserializeObject(response);
}
And I have the follow properties:
[Key]
public int Id { get; set; }
public int DisplayName { get; set; }
public int Avatar { get; set; }
To access a simple value from my JSON:
_naturalUserObject["users"][0]["name"];
The final question is: how can I store each value from my JObject in my model's properties and finally display it into my view?
Thanks in advance.
/EDIT/
My model now is:
public UserModel()
{
WebClient request = new WebClient();
string response = request.DownloadString(String.Format("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={0}&steamids={1}", ConfigurationManager.AppSettings["SteamApiKey"].ToString(), HttpContext.Current.Request.Cookies["SteamIdCookie"].Value));
string _naturalUserObject = JsonConvert.DeserializeObject<string>(response);
}
And one of my property is:
private string _avatar;
public string Avatar
{
get { return _avatar; }
set { _avatar = _naturalUserObject["response"]["players"][0]["avatar"]; }
}
But without success. =(
Lets say you have the following Json
string yourJsonString = "{\"FIRST_NAME\":\"Foo\",\"LAST_NAME\":\"Bar\"}";
You could model this Json as:
public class JsonModel
{
[JsonProperty("FIRST_NAME")]
public string FirstName {get; set;}
[JsonProperty("LAST_NAME")]
public string LastName {get; set;}
}
Note that you can use JsonPropertyAttribute to tell Json.Net what the property's corresponding json field is.
Now, that you have your model set up, can use JsonConvert.DeserializeObject<T>(...) to get a strongly typed instance of your json model.
JsonModel jsonModel = JsonConvert.DeserializeObject<JsonModel>(yourJsonString);
string firstName = jsonModel.FirstName; // Foo
string lastName = jsonModel.LastName; // Bar
As someone had mentioned, if you do it this way, you won't have to deal with JObject and moreover, it will force you to actually understand the structure of the json being returned.
Specifically in your example, you could store an object of type JsonModel and in your model's constructor initialize it using JsonConvert.DeserializeObject<T>. Your public properties could then just call into that JsonModel instance and get the appropriate values.
Here's a more detailed example. Assume you are getting back the json I had provided above and are using the same model we created before. Let's say you have a private field in your class of type JsonModel
private JsonModel jsonModel;
You can initialize it in your constructor:
public UserModel()
{
WebClient request = new WebClient();
string response = request.DownloadString(url);
jsonModel = JsonConvert.DeserializeObject<JsonModel>(response);
}
Now, your public properties can simply call into this JsonModel object.
public string FirstName
{
get { return jsonModel.FirstName; }
set { jsonModel.FirstName = value; }
}
I recommend ServiceStack.Text (available as a standalone NuGet package install, without the rest of the framework if you don't need it).
This adds two nice extension methods:
public class MyPOCO
{
public string Name {get; set; }
public int Age {get; set; }
}
And later:
string json = myPocoInstance.ToJson();
and:
MyPOCO instance = jsonString.FromJson<MyPOCO>();
Very fast too!
If you use Json.net you can deserialize directly into a clr object (which could be your viewmodel or model), you won't have to deal with JObject.
JsonConvert.DeserializeObject<myviewmodelormodel>(myjsondata);
If you get the object back as a string in JSON format. You can use the JSON de-serializer like this:
Put this into a static class this will become an extension method to all strings
public static T Deserialize<T>(this string json)
{
var obj = Activator.CreateInstance<T>();
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var serializer = new DataContractJsonSerializer(obj.GetType());
obj = (T) serializer.ReadObject(ms);
}
return obj;
}
EX:
this calls a url getting the result set in a string, then calls the
extension method to return an object.
the url returns MyPOCO
var stringValue = HttpGet(url);//how you get the value from a web service call
var myUserModel = stringValue.Deserialize<MyPOCO>();

Parsing JSON data in C#

I have a JSON data as follows
{"id": "367501354973","from": {
"name": "Bret Taylor",
"id": "220439" }
which is returned by an object(result) of IDictionary[String, Object]
In my C# code:
I have made a class for storing the JSON value which is as follows
public class SContent
{
public string id { get; set; }
public string from_name { get; set; }
public string from_id { get; set; }
}
My main C# function which stores the parses the JSON data and stores the value inside the class properties is as follows:
List<object> data = (List<object>)result["data"];
foreach (IDictionary<string, object> content in data)
{
SContent s = new SContent();
s.id = (string)content["id"];
s.from_name = (string)content["from.name"];
s.from_id = (string)content["from.id"];
}
When i execute this code, i get an exception saying System cannot find the Key "from.name" and "from.id"
When i comment the two lines (s.from_name = (string)content["from.name"];s.from_id = (string)content["from.id"];) my code runs fine.
I think i am not able to refer the nested JSON data properly.
Can anyone just validate it and please tell me how to refer nested data in JSON in C#?
Thanks
I'm not sure how you are parsing the JSON string. Are you using a class in the Framework to do the deserialization?
You could use the JavaScriptSerializer Class defined in the System.Web.Script.Serialization Namespace (you may need to add a reference to System.Web.dll)
Using that class, you would write your code like this:
public class SContent
{
public string id { get; set; }
public SFrom from { get; set; }
}
public class SFrom
{
public string name { get; set; }
public string id { get; set; }
}
Then deserialization looks like this:
var json = new JavaScriptSerializer();
var result = json.Deserialize<SContent>(/*...json text or stream...*/);
See JavaScriptSerializer on MSDN. You might also want to check out this similar question.

C# deserialize dynamic JSON

I have the following Json string that I need to deserialize.
{"123456789":
{"short_description":"Delivered",
"detail_description":"Your item has been delivered"
}
}
The first field "123456789" is an id number, so basically this value can be different depending on the data being queried.
I'm using C# in visual studio. Obviously because the value of the first field can change I can't use a predefined class to deserialize the JSON into because this field will be used as the class name but the field value won't match the class name.
Is there a way to deserialize this into some sort of dynamic class but still access the fields as if it was a predefined class?
Alternatively is there a way to deserialize this into a predefined class even thought the class name doesn't match?
The service providing this data is a third party one so i don't have any control over it.
Here is one way which I use in production code. It might not be perfect, but it gets the job done.
using using System.Web.Script.Serialization;
// .....
public object GetJson(string url)
{
var json = Get(url); // I have code that makes this work, it gets a JSON string
try
{
var deserializer = new JavaScriptSerializer();
var result = deserializer.DeserializeObject(json);
return result;
}
catch (ArgumentException e)
{
// Error handling....
}
}
The object you receive back will be a generic Map, List, or whatever depending on the structure of the JSON. If you know what structure to expect, this is very useful without writing a customized parser or target object type.
You could then enumerate the keys of the Map, for example, to find your key that varies. A wrapper or conversion would then provide a consistent API to the rest of your application layer. Something like:
public class Order {
public string OrderNum { private set; get; }
public string ShortDesc { private set; get; }
public string Desc { private set; get; }
public static Order FromJson(object jsonResult)
{
var m = jsonResult as Map<string, object>;
// Handle errors, but I am not
var firstPair = m.First();
var detail = firstPair.Value as Map<string, object>;
var dummy = new Order()
{
OrderNum = firstPair.Key,
ShortDesc = detail["short_description"].ToString();
Desc = detail["detail_description"].ToString();
}
return dummy;
}
}
I liked answer above so I refactored it a bit. You'll need references to System.Web.Extensions.dll and System.Web.Script.Serialization.
Here's the class:
public class Order
{
public string OrderNum { private set; get; }
public string ShortDesc { private set; get; }
public string Desc { private set; get; }
public static Order FromJson(string jsonResult)
{
JavaScriptSerializer js = new JavaScriptSerializer();
// Should deserialize right to Dictionary<string, object>
// var result = ((Dictionary<string, object>)js.Deserialize<dynamic>(jsonResult)).First();
var result = js.Deserialize<Dictionary<string, object>>(jsonResult).First();
var detail = (Dictionary<string, object>)result.Value;
return new Order()
{
OrderNum = result.Key,
ShortDesc = detail["short_description"].ToString(),
Desc = detail["detail_description"].ToString()
};
}
}
And how to call it:
string json = "{\"123456789\": {\"short_description\":\"Delivered\", \"detail_description\":\"Your item has been delivered\" } }";
Order o = Order.FromJson(json);
You'll need to implement error handling on your own however.

Categories