So i have an api that returns a list of objects.
The objects might not always have all properties set and i would need to determine which property values was present in Json response.
For example User object, in some cases it can have Firstname set in some cases not so how can i know that without manually going through Json response and checking each individual property name for existence.
An example Json
{
"id": 250,
"gender": "M",
"country_code": "GR",
"create_datetime": "2018-11-08T17:20:56+0800"
}
And then
{
"id": 250,
"create_datetime": "2018-11-08T17:20:56+0800"
}
I use JsonConvert.DeserializeObject for de-serialization.
Is manual approach is only way ?
I don't think JsonPropertyAttribute is for your case, you actually need to serialize all properties, but when you need it, you deserialize it, then just want to return a subset to client.
You can return anonymous json format for client without strong typing it, especially when you just want to return a subset of a result.
For example, the user you get is:
var user = new User
{
Id = "1",
FirstName = "Foo",
LastName = "Bar",
Gender = "None",
Orders =[{ Id, time,.....}]
};
but if you want to return a subset:
return Json(new
{
Id = user?.Id,
Orders = user?.Orders?.Select(x => x.Id) ?? Enumerable.Empty<int>()
})
then client will get:
{
Id: xxx,
Orders: [1,2,3,4]
}
In general, the best way I've discovered to check JSON payloads for membership is to deserialize them and examine the contents. In your case:
User resp = JsonConvert.DeserializeObject<User>(content);
bool userHasFirstName = !string.IsNullOrWhitespace(resp.Firstname);
But wait, you may ask, what happens if I have non-nullable fields and they're not present on the payload? To this, I have two responses:
Don't have non-nullable fields on anything you expect to deserialize from JSON if you don't know for sure that you'll have a value there. Or,
Add [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] and [DefaultValue(YOUR_DEFAULT_VALUE)] attributes to the property
Note that you can always examine the JSON response as a text string and there are libraries that will let you examine the JSON response as an object. What I have provided is the simplest and broadest use case.
Related
No idea where to begin with this, so I don't have any sample code.
I need to change the name of a property in a json document.
var json = (#"{""id"":""12"",
""title"":""My Title"",
""Chunks"":[
{
""id"":""137"",
""title"":""Title"",
""description"":""null"",
""selections"":[
{
""id"":""169"",
""title"":""Choice"",
""sort_order"":""null"",
""questions"":[
]
}
]
}
]
}
}
}");
I need to change the "id" that's got the value of 12 to "document_id" and leave the other ids alone. Are there any C# libraries like NewtonSoft that allow you to change the property rather than the property value. Seems like a common scenario but I haven't seen anything close to what I'm trying to do. I suppose I could convert the json to a string and do a replace, but that doesn't seem very elegant.
An approach using Newtonsoft.Json.Linq.JObject would look something like:
var obj = JObject.Parse(json);
obj["document_id"] = obj["id"]; // create new property called "document_id"
obj.Remove("id"); // remove the "id" property
Console.WriteLine(obj);
Also note that your JSON is not valid. It has two extra } at the end.
Assuming you would want to replace all the keys when there could be more than one node with key as "id" and value "12", you could use Linq to identify Tokens with Key "Id" and Value "12" and then use Add/Remove methods for creating a new node with different name.
For example,
JToken node = JToken.Parse(json);
var jObjectsWithTitle = node
.SelectTokens("$..*")
.OfType<JObject>()
.Where(x => x.Property("id") != null && Convert.ToInt32(x.Property("id").Value) == 12);
foreach(var item in jObjectsWithTitle)
{
item.TryGetValue("id",out var currentValue);
item.Add("document_id",currentValue);
item.Remove("id");
}
I am trying to extract data from D2L for the dropbox submissions using API's I have a link which returns a Json Array as per told on the documentation and from this Array I just need the Id feild nothing else.
I have tried to convert this Array into dynamic Object but that didn't help.
Here is my code.
var client = new RestClient("https://" + LMS_URL);
var authenticator = new ValenceAuthenticator(userContext);
string Link = "/d2l/api/le/1.12/UnitID/dropbox/folders/UniID/submissions/";
var request = new RestRequest(string.Format(Link));
request.Method = Method.GET;
authenticator.Authenticate(client, request);
var response = client.Execute(request);
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(response.Content);
Response.Write(jsonString);
//var splashInfo = JsonConvert.DeserializeObject<ObjectD>(response.Content);
dynamic jsonResponse = JsonConvert.DeserializeObject(response.Content);
var parsedObject = jsonResponse.Parse(jsonResponse);
string json = jsonResponse;
var popupJson = parsedObject["id"].ToString();
What my goal is that grab the list of ID's from this response and loop through them, these ID's are the key for my next API route.
Here is a glance at what I get from the response:
[
{
"Id": 2021,
"CategoryId": null,
"Name": "Graded Assignment: Part 1",
"CustomInstructions": {
"Text": "Directions:\r\nCOMPLETE the following TestOut Activities\r\n\r\n1.2.10 Practice Questions\r\n1.3.8 Practice Questions\r\n\r\nGo to TestOut to complete the Assignment."
//Other properties omitted
}
//Other properties omitted
}
]
The outermost container in the JSON returned is an array, so you need to deserialize to a collection type as specified in the Newtonsoft's Serialization Guide: IEnumerable, Lists, and Arrays.
Since you only care about the Id property of the object(s) in the array, you can use JsonConvert.DeserializeAnonymousType to deserialize just the interesting value(s):
var ids = JsonConvert.DeserializeAnonymousType(response.Content, new [] { new { Id = default(long) } })
.Select(o => o.Id)
.ToList();
And if you are certain the outermost array will contain exactly one item, you can do:
var id = JsonConvert.DeserializeAnonymousType(response.Content, new [] { new { Id = default(long) } })
.Select(o => o.Id)
.Single();
Alternatively, if you think you will later need to deserialize additional properties, you could make an explicit data model as follows:
public class ObjectId
{
public long Id { get; set; }
}
And do:
var ids = JsonConvert.DeserializeObject<List<ObjectId>>(response.Content)
.Select(o => o.Id)
.ToList();
Notes:
You will need to determine from the API documentation whether long or int is more appropriate for the Id value.
As a general rule I recommend not parsing to dynamic because you lose compile-time checking for correctness. When dealing with completely free-form JSON, parsing to JToken may be a better solution -- but your JSON appears to have a fixed schema, so neither is necessary.
Demo fiddle here.
I wish to write some C# which allows the client to provide a JSON string and query string. The query string would then be used to address values in the JSON object.
For example, if I had this JSON:
{
"head": "big",
"fingers": [
"one", "thumb",
"two", "ring"
],
"arm": {
"elbow", "locked"
}
}
And this query string:
"fingers.two"
I would want to return the value "ring".
Is this (or something like it) possible in C#?
I have tried using the ExpandoObject class, but this does not allow dynamic runtime inspection:
var json = JsonConvert.DeserializeObject<ExpandoObject>(jsonStr);
As far as I can tell, the discovery of values on the json variable needs to be done at code time, rather than runtime, which means I cannot dynamically find values being queried for.
JSONPath does this
Assuming the following JSON (fixed a few syntax errors in the original)
{
"head": "big",
"fingers": {
"one":"thumb",
"two":"ring"
},
"arm": {
"elbow": "locked"
}
}
And this query
MyJObjectOrToken.SelectToken("fingers.two")
You will get the following output:
[
"ring"
]
It should be trivial then to extract the value as a string using JSON.Net methods and return the result to your user.
Support for JSONPath is built into JSON.Net
https://www.newtonsoft.com/json/help/html/SelectToken.htm
I have the following code:
var definition = new { result = "", accountinformation = new[] { "" , "" , "" } };
var accountInformationResult = JsonConvert.DeserializeAnonymousType(responseBody, definition);
The account information structure comes back from an endpoint as an array with each element being another array containing 3 strings. So the embedded array is not in a key value pair format. With the above definition accountinformation returns null. What should the syntax be for this structure?
For reference this is what is going on in the php endpoint.
$account_information[] = array( $billing_company, $customer_account_number, $customer_account_manager );
This first line is in a loop. Hence the multi-dimensional array.
echo json_encode(array('result'=>$result, 'account_information'=>$account_information));
I know I could use dynamic but why the extra effort?
I assume your json will look something like this:
{
"result": "the result",
"account_information": [
["company1", "account_number1", "account_manager1"],
["company2", "account_number2", "account_manager2"]
]
}
In that case you should be able to deserialize with the following definition (note the underscore in account_information:
var definition = new { result = "", account_information = new List<string[]>() };
In json you are allowed to add extra properties as you please while your data model changes. So if you define a data model that does not include one of these properties, the property will simple be ignored. In your case the definition does not have a property called account_information (exactly), so this part of the json is ignored while deserializing.
EDIT:
If it's going to be an anonymous abject anyway, you may also consider parsing into a JObject:
var obj = JObject.Parse(responseBody);
string firstCompany = obj["account_information"][0][0];
string secondCompany = obj["account_information"][1][0];
[{"id":"PROCESS_ROOT_NODE","text":"TEMPLATE - 3333(2)","icon":"fa fa-list fa-color-graylt","li_attr":{"id":"PROCESS_ROOT_NODE","__type":"li_attr:#SomeNamespace.JsonDataContractClasses","class":" ps_node_li "}}]
I slimmed the object down alot.
Basically when the '__type' is not in the first position, before 'id'. The deserialize will throw an error.
I have all the DataContract stuff setup correctly, with known types.
I've tested in a console app, serializing, then taking that string back thru the deserialize and it works perfectly. The only difference is the location of the '__type'. This is a known MS issue.
Documented at https://msdn.microsoft.com/en-us/library/bb412170(v=vs.110).aspx
Tried a string replace, which does work. and the DataContractJsonSerializer did not care if '__type' key was in there twice.
content = content.Replace("\"li_attr\":{", "\"li_attr\":{\"__type\":\"li_attr:#Payce.Common.AnonymousClasses.JsonDataContractClasses\",");
Just looking for the best way to move the __type to the first position.
You can use Json.Net to manipulate your json
var jArr = JArray.Parse(jsonstring);
var attrs = jArr.Select(x => x["li_attr"]).ToList();
attrs.ForEach(attr =>
{
var type = attr["__type"].Parent;
type.Remove();
(attr as JObject).AddFirst(type);
});
var newjson = jArr.ToString(Newtonsoft.Json.Formatting.Indented);
Output of this code is
[
{
"id": "PROCESS_ROOT_NODE",
"text": "TEMPLATE - 3333(2)",
"icon": "fa fa-list fa-color-graylt",
"li_attr": {
"__type": "li_attr:#SomeNamespace.JsonDataContractClasses",
"id": "PROCESS_ROOT_NODE",
"class": " ps_node_li "
}
}
]
But I would recommend to use Json.Net all the way instead of just converting your json to the desired format.
Besides the string replacement.
I used an answer from Dave R - stack overflow
Use the JSON.stringify(obj, replacer array) replacer method.
var json = JSON.stringify(o, ['__type', 'id', 'parent', 'text', 'type', 'children', 'data', 'li_attr', 'a_attr', 'state', 'class', 'descr', 'display_priority', 'action_area_id', 'action_user_type_id']);
Its a little bit of a pain to list all the keys, but it also acts like a filter, so i only return what i need as well.
And since i put '__type' first, in all the objects and sub objects, this key was listed first after the stringify.