Reference Object Property with JSON.Net when property has special characters - c#

I am using JSON.Net to update an object within an XML File. This works great! However, I need to update a property that has some special charecters in it and I can't find any information out there on how to do this with JSON.Net.
XML
<ProfileSettings>{
"Name": "Default",
"Status": "New",
"DeploymentVariants": {
"SPSite": {
"Signature": "SPSite",
"Type": "SPSite",
"Label": "SharePoint Site",
"Source": "Solution",
"DefaultValue": "http://google.com",
"Value": "http://google.com"
},
"Process/Participant[#Name=\"Manager\"]>User": {
"Signature": "Process/Participant[#Name=\"Manager\"]>User",
"Type": "SPUser",
"Label": "User for swim lane Manager",
"Source": "Swimlane",
"DefaultValue": "John Smith",
"Value": "John Smith"
}
},
"DeploymentScripts": {},
"SPList": "mySPList"
}</DeploymentProfile>
In order to update the DefaultValue in the SPSite Object, I can use JSON.Net like so:
dynamic fromSolution = JsonConvert.DeserializeObject(profileObject);
fromSolution.DeploymentVariants.SPSite.DefaultValue = txtSPSite.Text;
However, this won't be true if I'm trying to access the Process/Participant[#Name=\"Manager\"]>User object. How can I access a property when it's got special characters like this one?
fromSolution.DeploymentVariants.Process.DefaultValue did not work and obviously including special charecters within that will just result in runtime errors.

JObject implements IDictionary<string, object>. You can use dictionary syntax:
fromSolution.DeploymentVariants["Process/Participant[#Name=\"Manager\"]>User"].DefaultValue

Related

.net Core & Swashbuckle/Swagger: How to provide raw json example?

I have a WebAPI controller with an operation returning a JSON schema. This JSON return value cannot be created by serializiation, so I designed the operation method as follow:
[HttpGet("{serviceName}/contract")]
[SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(object))]
public IActionResult GetContract(string serviceName)
{
return Content("{ \"type\": \"object\" }", "application/json"); // for example ...
}
Now I like to have a or some documented return values for Swagger. But I'm unable to do that. There is the SwaggerRequestExample attribute, but as said before, this requires a return type which in my case is not applicable.
Basically I search for a way of something like that (just dynamic):
[SwaggerResponseExample((int)HttpStatusCode.OK, "{\"anyJson\": \"Yes, I am!\"}")]
Or of course, even better like that:
[SwaggerResponseExample((int)HttpStatusCode.OK, RawJsonFabricType="TypeName", RawJsonStaticMethod="MethodName")]
Use case: The JSON schemas I need to return in operation method are stored in a database and are not created within the program code itself.
A concrete example of such a JSON schema value is:
{
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"lastName": {
"type": "string",
"description": "The person's last name."
},
"age": {
"description": "Age in years which must be equal to or greater than zero.",
"type": "integer",
"minimum": 0
}
}
}
Help will be very appreciated.
Thanks!
I'm using c#.net core 6.
Afer trying arround I came to the solution:
First: Add ExampleProvider and use generic type JsonDocument (from System.Text.Json):
public class ServiceDemandContractExampleProvider : IExamplesProvider<JsonDocument>
{
/// <inheritdoc/>
public JsonDocument GetExamples()
{
var jsonToShow = JsonDocument.Parse(#"{
""$id"": ""https://example.com/person.schema.json"",
""$schema"": ""https://json-schema.org/draft/2020-12/schema"",
""title"": ""Person"",
""type"": ""object"",
""properties"": {
""firstName"": {
""type"": ""string"",
""description"": ""The person's first name.""
},
""lastName"": {
""type"": ""string"",
""description"": ""The person's last name.""
},
""age"": {
""description"": ""Age in years which must be equal to or greater than zero."",
""type"": ""integer"",
""minimum"": 0
}
}
}");
return jsonToShow;
}
}
To JsonDocument.Parse put whatever JSON (in my case loaded content from database).
Then add the follow attributes to the operation method:
[SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(object))]
[SwaggerResponseExample((int)HttpStatusCode.OK, typeof(ServiceDemandContractExampleProvider))]
And it works:

Select type object from json?

I have a problem with correct parse object from json query. I read something about JObject. Now i have two Model for example like Car and MotorBike. Query result is:
"Vehicles":
[
{
"Id": 1,
"title": "test",
"price": "4000",
"type": "Car"
},
{
"Id": 1,
"title": "test",
"price": "4000",
"drivingLicenseCat" "A",
"type": "MotorBike"
}
]
how can i parse to custom model by type
How much control over the JSON do you have? If you are generating the JSON out of, say, web api correctly, it would come through more like:
"Vehicles":
[
{
"Id": 1,
"title": "test",
"price": "4000",
"$type": "YourNamespace.Car, YourNamespace"
},
{
"Id": 1,
"title": "test",
"price": "4000",
"drivingLicenseCat" "A",
"$type": "YourNamespace.MotorBike, YourNamespace"
}
]
and then it would be automatically deserialized when you bring it in...
Ensure, in the model that you are serializing and sending out as JSON, that you mark it up like so:
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.All)]
This will add the type names to the objects as they are serialized. I should also note, that this is with Newtonsoft as your Json library. I'm not sure about the built-in Json.

Azure Search - Accent insensitive analyzer not working when sorting

I'm using Azure Search. I have a model with a property with this attributes
[IsRetrievable(true), IsSearchable, IsSortable, Analyzer("standardasciifolding.lucene")]
public string Title { get; set; }
I want the search to be accent insensitive. Although it is working when searching/filtering, it is not working when sorting the results. So, If I have words that start with an accent and I sort alphabetically, those results appear at the end of the list.
I verified your use case by creating an index with Id and a Title field that uses the standardasciifolding.lucene analyzer. I then submitted the 4 sample records via the REST API:
{
"value": [
{
"#search.action": "mergeOrUpload",
"Id": "1",
"Title" : "øks"
},
{
"#search.action": "mergeOrUpload",
"Id": "2",
"Title": "aks"
},
{
"#search.action": "mergeOrUpload",
"Id": "3",
"Title": "áks"
},
{
"#search.action": "mergeOrUpload",
"Id": "4",
"Title": "oks"
}
]}
I then ran a query with $orderby specified. I used Postman with variables wrapped in double curly braces. Replace with relevant values for your environment.
https://{{SEARCH_SVC}}.{{DNS_SUFFIX}}/indexes/{{INDEX_NAME}}/docs?search=*&$count=true&$select=Id,Title&searchMode=all&queryType=full&api-version={{API-VERSION}}&$orderby=Title asc
The results were returned as
{
"#odata.context": "https://<my-search-service>.search.windows.net/indexes('dg-test-65224345')/$metadata#docs(*)",
"#odata.count": 4,
"value": [
{
"#search.score": 1.0,
"Id": "2",
"Title": "aks"
},
{
"#search.score": 1.0,
"Id": "4",
"Title": "oks"
},
{
"#search.score": 1.0,
"Id": "3",
"Title": "áks"
},
{
"#search.score": 1.0,
"Id": "1",
"Title": "øks"
}
]
}
So, the sort order is indeed a, o, á, ø which confirms what you find. The order is inversed if I change to $orderby=Title desc. Thus, the sorting appears to be done by the original value and not the normalized value. We can check how the analyzer works, by posting a sample title to the analyzer with a POST request to
https://{{SEARCH_SVC}}.{{DNS_SUFFIX}}/indexes/{{INDEX_NAME}}/docs?search=*&$count=true&$select=Id,Title&searchMode=all&queryType=full&api-version={{API-VERSION}}&$orderby=Title asc
{ "text": "øks", "analyzer": "standardasciifolding.lucene" }
Which produces the following tokens
{
"#odata.context": "https://<my-search-service>.search.windows.net/$metadata#Microsoft.Azure.Search.V2020_06_30_Preview.AnalyzeResult",
"tokens": [
{
"token": "oks",
"startOffset": 0,
"endOffset": 3,
"position": 0
},
{
"token": "øks",
"startOffset": 0,
"endOffset": 3,
"position": 0
}
]
}
You could try to define a custom analyzer which produces a normalized version, but I am not sure it will work. For example, the sorting does not appear to support case-insensitive sorting which would be related to this use case where multiple characters should be sorted as if they were a normalized version. E.g. a and A cannot be sorted as the same character according to this user voice entry (feel free to vote for it).
WORKAROUND
The best workaround I can think of is to process the data yourself. Let Title contain the original title, and then create another field called TitleNormalized where you store the normalized version. In your application you would then query with $orderby on the TitleNormalized field.
There is a new feature that allows you to enable normalization while filtering. Please check out the Text normalization for case-insensitive filtering, faceting and sorting feature that's in Preview.
You can update your index to use this "normalizer" feature for the fields in which you'd like case-insensitive order-by operations.
You don't need a separate field TitleNormalized anymore. You can add "normalizer": "standard" to the Title field, and $orderBy=Title will sort in the order you'd like, ignoring casing and accents.
The "standard" normalizer is pre-defined. If you'd like other filters to be applied, please look at predefined and custom normalizers
"index": {
"name": "your-name-here",
"fields": [
{"name": "Title", "type": "Edm.String", "filterable": true, "sortable": true, "facetable": false, "searchable": true, "normalizer":"standard"}
]
}

Deserializing random json file in c#

i am trying to take in a json file and iterate over some of the values.
The file is 73000 lines long, and i only need a few values.
Here is an example of the json file:
{
"value" : "key"
"value" : "key"
"Owner": {
"UserId": xxx,
"Username": "xxx",
},
"Items": [
{
"value": "key",
"value": "key",
"Comment": "",
"value": {
"value": {
"value": "key",
"value": "key",
"value": "key",
}
},
"CustomFields": [
{
"Name": "something",
"Content": "true"
}
]
}
]
}
How do i load this in without defining the object? i need 5 values per item, from different nesting levels, though i dont want to define the class as the file is more than 70.000 lines.
I tried doing it like this:
var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
But that way i will get an error when i try to itterate over items, as items value will now be a string!
foreach (Dictionary<string, string> result in values["items"])
Is there a way to load the file, defining the class based on the json schema?
I need values from different levels in the file, some at root level some nested.

How can I $filter on Dictionary<string, string> using OData?

I have an OData query enabled action on my Controller that Returns an Asset.
C# Model.
var asset = new Asset()
{
Id = Guid.NewGuid().ToString(),
Name = "Cool Asset Yo",
Url = "http://test/test.asset",
Tags = new[] {"test"},
Properties = new Dictionary<string, string>
{
{"platform", "android"},
{"dim_depth", "1.0"},
{"dim_height", "1.0"},
{"dim_width", "1.0"},
{"item_type", "Trim"}
}
}
Returned JSON
[
{
"name": "Cool Asset Yo",
"properties": {
"platform": "android",
"dim_depth": "1.0",
"dim_height": "1.0",
"dim_width": "1.0",
"item_type": "Trim"
},
"tags": [
"test"
],
"url": "http://test/test.asset",
"id": "77d9b9df-4f4b-4fad-a1d3-af5075d52a62",
}
]
Example Queries that work!
api/Asset?$filter=startswith(name, 'Cool')
api/Asset?$filter=tags/any(tag eq 'test')
api/Asset?$filter=id eq '77d9b9df-4f4b-4fad-a1d3-af5075d52a62'
And NOW for the fail :-(
api/Asset?$filter=properties/platform eq 'Android'
Error: The parent value for a property access of a property 'platform' is not a single value. Property access can only be applied to a single value.
api/Asset?$filter=properties/any(props: props/platform eq 'Android')
Error: Could not find a property named 'platform' on type 'System.Collections.Generic.KeyValuePair_2OfString_String'.
api/Asset?$filter=properties/any(keyValue: keyValue('platform') eq 'Android')
Error: An unknown function with name 'keyValue' was found. This may also be a function import or a key lookup on a navigation property, which is not allowed.
api/Asset?$filter=properties/any(keyValue: keyValue eq 'Android')
Error: A binary operator with incompatible types was detected. Found operand types 'System.Collections.Generic.KeyValuePair_2OfString_String' and 'Edm.String' for operator kind 'Equal'.
api/Asset?$filter=properties['platform'] eq 'Android'
Error: Syntax error at position 31 in 'properties['platform'] eq 'Android''.
How do I get a list of assets with the 'platform' of 'Android'? I see examples of in the Microsoft Documents of Generic Dictionaries being used in a model, I don't see any $filter examples.
In your scenarios, "Properties" looks a dictionary property, but the dictionary property is not a built-in property in OData.
Besides, your payload looks a normal JSON serialized output. It's not odata payload.
You said you saw examples of in the Microsoft Documents of Generic Dictionaries being used in a model, it's a usage of dynamic property. Please pay attention that "It's different between your scenario (dictionary) and the dynamic property".
Most important, Web API OData now supports to filter on the dynamic property.
See my test cases in the commit
Hope it can help you.
Sam Xu is correct, a dictionary property is not supported in OData, also dynamic property did not work in my scenario. I was forced to change my properties bag to be a list of a custom key-value type.
C# Model.
var asset = new Asset()
{
Id = Guid.NewGuid().ToString(),
Name = "Cool Asset Yo",
Url = "http://test/test.asset",
Tags = new[] {"test"},
Properties = new List<KeyValue>
{
new KeyValue("platform", "android"),
new KeyValue("dim_depth", "1.0"),
new KeyValue("dim_height", "1.0"),
new KeyValue("dim_width", "1.0"),
new KeyValue("item_type", "Trim")
}
}
Returned JSON
[
{
"name": "Cool Asset Yo",
"properties": [
{
"key": "platform",
"value": "android"
},
{
"key": "dim_depth",
"value": "1.0"
},
{
"key": "dim_height",
"value": "1.0"
},
{
"key": "dim_width",
"value": "1.0"
},
{
"key": "item_type",
"value": "Trim"
}
],
"tags": [
"test"
],
"url": "http://test/test.asset",
"id": "77d9b9df-4f4b-4fad-a1d3-af5075d52a62",
}
]
Example Queries that work!
api/Asset?$filter=properties/any(keyValue: keyValue/key eq 'platform' and keyValue/value eq '50129486')

Categories

Resources