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
Related
I have an index filled by data with this schema:
{
"name" : "string",
"book": "string"
}
And I am working with ElasticSearch with Nest and C#.
Now because of the business requirement book field must be an array and I Update my query model C# POCO class but when I run my query I am getting this error:
expected:'[', actual:'"test"', at offset:2248
Now I want to Update my schema to convert the book to array type. I try to use script and mapping but could not find a good solution? is there any working method to perform this change in ElasticSearch?
ES has built in functionally for docs update by query https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html.
In case above query will look like this:
POST my-index-000001/_update_by_query
{
"script": {
"source": " ctx._source.book = [ctx._source.book] ",
"lang": "painless"
},
"query": {
"match_all":{}
}
}
you cannot update or change field type in mapping(schema) of indices that has been created.
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html#updating-field-mappings
if you need change type, you should create new indices and use Reindex API.
I am writing a Web API with requirement where need to pass result class property values as array of Json in response of GET request.
Property class which will be passed as a actual result with Ok Status with object. ( I am mocking actual requirement)
public class ABC
{
public string Name {get;set;}
public string Address{get;set;}
}
I am following default JSONfor matter option which are available in dotnet core web api and it is converting all class attribute into single json element.
{
"Person" :
[
{
"Name": "ABCD",
"Address": "INDIA"
}
]
}
My requirement is to have data in Json format with array as below -
{
"Person" :
[
{"Name": "ABCD"},
{"Address": "INDIA"}
]
}
using Newtonsoft.Json;
use this method to convert obj to string:
JsonConvert.SerializeObject(object)
use this method to convert string to obj:
JsonConvert.DeserializeObject(string)
=== Updated my answer to reflect clarified details ===
Solution with Json.Net:
To get the JSON result that you're looking for, you'll need to create a custom serializer or build your JSON object with dynamic JTokens.
Here's an example using a dynamic JObject:
https://dotnetfiddle.net/EyL5Um
Code:
// Create sample object to serialize
var person = new ABC() { Name = "ABC", Address = "India" };
// Build JSON with dynamic JTokens
dynamic jsonObj = new JObject();
var token = new JArray();
token.Add(new JObject(
new JProperty("Name", person.Name)));
token.Add(new JObject(
new JProperty("Address", person.Address)));
jsonObj.Person = token;
// Print result to console
Console.WriteLine(jsonObj.ToString());
Note
In this form, the code above is not a scalable solution. But it should provide you with a starting point to then build up an iterative approach for the data you're working with.
References
Newtonsoft Documentation - Create JSON w/ Dynamic
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.
[{"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.
I have a 'simple' scenario: Read some JSON file, Filter or change some of the values and write the resulting json back without changing the original formatting.
So for example to change this:
{
"type": "FeatureCollection",
"crs": {
"type": "EPSG",
"properties": {
"code": 28992
}
},
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
149886.192,
374554.705
],
[
149728.583,
374473.112
],
[
149725.476,
374478.215
]
]
]
}
}
]
}
Into this:
{
"type": "FeatureCollection",
"crs": {
"type": "EPSG",
"properties": {
"code": 28992
}
},
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates":
[
149886.192,
374554.705
]
}
}
]
}
I've tried JSON.Net by newtonsoft among others but the only this I can find is:
read into object
write object to json
But I'm missing the 'change the object' step. Any hints?
Update
Here's what I've tried so far:
JToken contourManifest = JObject.Parse(input);
JToken features = contourManifest.SelectToken("features");
for (int i = 0; i < features.Count(); i++)
{
JToken geometry = features[i].SelectToken("geometry");
JToken geoType = geometry.SelectToken("type");
JToken coordinates = geometry.SelectToken("coordinates");
geoType = "Point";
}
But this only changes the value of the geoType variable. I'd expected to change the value inside the geometry as well. I need a reference, not a copy! Is this possible?
Update
I am currently off this project but I'd like to give my feedback to the answerers. Though I like the simplicity of Shahin, I like the more formal approach of L.B. a bit better. I personally don't like using string values as functional code, but that's just me. If I could accept both answers: I would. I guess Shahin wil have to make due with 'just' an upvote.
dynamic contourManifest = JObject.Parse(input);
foreach (var feature in contourManifest.features)
{
feature.geometry.Replace(
JObject.FromObject(
new {
type = "Point",
coordinates = feature.geometry.coordinates[0][0]
}));
}
var newJson = contourManifest.ToString();
I know this has already been answered but I thought I had a solution others might find interesting.
I had a pretty large stringified JSON object that I received from a customer and needed to manipulate in C# and then return in string form back to the calling application.
It didn't make sense to model every aspect of the object, many parts that I wasn't planning on manipulating were changing often and I couldn't be expected to update my application every time the caller modified portions of their JSON object I wasn't being asked to manipulate. So I tried this, it's a bit ugly but it worked well:
Create a class (myClass) representing just the section you want to manipulate.
Using Newtonsoft, create a dynamic version of the stringified JSON object:
dynamic jsonObj = JsonConvert.DeserializeObject(stringifiedJsonObject);
Build your replacement object using the class you created above (myClass). Then serialize that object using
string stringPartialJsonObj = JsonConvert.SerializeObject(myClass);
Next, (and this is the trick) deserialize the object you just created. Now it's the same type as your source.
dynamic partialJsonObj = JsonConvert.Deserialize(stringPartialJsonObj);
Imagine (for the sake of this demonstration) in the original Json object, I needed to modify the object in obj.ConfigurationData.Configuration1.Data. This is how I'd do it:
jsonObj.ConfigurationData.Configuration1.Data = partialJsonObj;
Finally, I'd re-serialize the whole thing and send it back to the user:
return JsonConvert.SerializeObject(jsonObj);
It's a bit clunky, but it works. Story of my life :-)
If you don't want using any entity that representing your JSON, you can deserialize to Dictionary by using json.net and modify dictionary, then serialize it to JSON by using Json.net.
Using Json.net you have to create the entities representing your json
Deserialize the json into those enties like Json.Convert<FeatureCollection>(json)
Change the entities
Convert it back to json.