I'm setting up an API which dynamically advertises services as part of its response, letting the consumer know what else they can do with the object. I can't work out how to inject the parent's objects type and ID into the service URI.
I'm using attributes and custom JSON.NET IValueProviders to create the URIs (which is working fine for other dynamic examples), but in this case I need a property from outside the object.
Classes looks similar to:
public class Person
{
public Guid ID { get; set; }
public string Type { get; set; }
public string Name { get; set; }
[ObjectUri]
public string Uri { get; set; }
...
public IEnumerable<Service> Services { get; set; }
}
public class Service
{
public string ServiceName { get; set; }
[ServiceUri]
public string ServiceUri { get; set; }
}
I'm using a custom contract resolver to add the custom IValueProvider (ObjectUriValueProvider) which renders the object URI (which is obviously just using information from the same object).
However, because the contract resolver is operating at the level of the abstract model, there's no way to pass concrete values pertaining to the parent object to ServiceUriValueProvider.
Ideal output:
{
"id": "ae330dc8-0217-4c58-b64b-44a1c7ba4f46",
"type": "Person",
"name": "Fred",
"uri": "https://server/api/person/ae330dc8-0217-4c58-b64b-44a1c7ba4f46"
...
"services": [
{
"serviceName": "ListNotes",
"serviceUri": "https://server/api/notes/person/ae330dc8-0217-4c58-b64b-44a1c7ba4f46"
},
{
"serviceName": "ListContactDetails",
"serviceUri": "https://server/api/contacts/person/ae330dc8-0217-4c58-b64b-44a1c7ba4f46"
}
]
}
The actual results at the moment don't include the type and id being correctly populated into
{
"services": [
{
"serviceName": "ListNotes",
"serviceUri": "https://server/api/notes/{type}/{id}"
},{
"serviceName": "ListContactDetails",
"serviceUri": "https://server/api/contacts/{type}/{id}"
}
]
}
Related
I have a dictionary in my type, which I want to get as input on controller:
using Newtonsoft.Json;
namespace LM.WebApp.Models.ApiModels;
[JsonDictionary]
public class Values
{
public Dictionary<string, string> values { get; set; }
}
public class Data
{
public bool is_active { get; set; }
public IList<string> labels { get; set; }
public string name { get; set; }
public Values values { get; set; }
}
public class DictionaryImportApiModel
{
public IList<Data> data { get; set; }
}
I'm passing on input this test JSON:
{
"data":
[
{
"is_active": true,
"labels": [
"SYSTEM",
"EXTERNAL"
],
"name": "MEDICATION_REQUEST_INTENT",
"values": {
"order": "Замовлення ліків",
"plan": "План застосування"
}
}
]
}
And getting error from question title and null dictionary in inbound object. I have changed Newtonsoft.Json serializer to Microsoft.AspNetCore.Mvc.NewtonsoftJson and removed attribute [JsonDictionary] from Values class and added .AddNewtonsoftJson() just after AddControllersWithViews in Startup. Exception is gone, but dictionary in inbound object is still null. Is it necessary to use custom converters (like this)
to handle dictionary?
Change values type of Data model to Dictionary<string, string>:
public class Data
{
public bool is_active { get; set; }
public IList<string> labels { get; set; }
public string name { get; set; }
public Dictionary<string, string> values { get; set; }
}
One of the conventions supported by main .NET json serializers (both Newtonsoft.Json and System.Text.Json, maybe some others, but have not worked with them) is converting json object to and from Dictionary, so you don't need extra wrapper class Values.
P.S.
Unless you have specific naming conventions in your project - there is no need to name classes properties the same way as they are named in the source json. For Newtonsoft.Json you can either mark properties with JsonPropertyAttribute or trying to setup corresponding NamingStrategy in serializer settings (System.Text.Json has similar options).
I am trying to serialize JSON objects received from an API in a cli app. I'm having issues understanding how to create the objects in .NET for JSON objects which have an indented structure.
For example, this is fine:
{"status": "ok" }
public class Success
{
public string status { get; set; }
}
But something like this is where I'm stuck and both of the examples from the below return null when the client API receives them.
[
{
"id": "some_uuid_string_1",
"message": "hello"
},
{
"id": "some_uuid_string_2",
"message": "world"
}
]
Attempted solution
public class Received
{
public Dictionary<string,string> received { get; set; }
}
Alternatively I also tried a simpler structure, leaving out the explicit names and just using the IDs and values, which is closer to what my app requires and lets me make smaller requests.
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
For this example I tried this, a list of key value pairs in the form of a dictionary.
public class Message
{
public Dictionary<string,string> message { get; set; }
}
public class Received
{
public List<Message> received { get; set; }
}
How can I create objects in C# for these two structures? One indented with set names and one 'generic' with no set names.
public class MyClass
{
public string id { get; set; }
public string message { get; set; }
}
[
{
"id": "some_uuid_string",
"message": "hello"
},
{
"id": "some_uuid_string",
"message": "world"
}
]
Deserializes to a List<MyClass> or MyClass[]
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
Deserializes to
public class MyClass
{
public string some_uuid_string_1 { get; set; }
public string some_uuid_string_2 { get; set; }
}
or Dictionary<string, string>
The reason your Received class solution didn't work is because it is expecting a JSON property of received, as your class has a property named received, but the JSON does not.
This is the same issue with your Message class. Your class has property message whereas your JSON does not.
create a class
public class MessageId
{
public string id { get; set; }
public string message { get; set; }
}
you can deserialize your json
using Newtonsoft.Json;
var messages=JsonConvert.DeserializeObject<List<MessageId>>(yourJson);
I am consuming a REST API - which returns JSON. I deserialize the JSON using NewtonSOFT JSON in C#.
The returned JSON contanins an "Answer" object that contain another "Answer" object - problem is that the 2 "Answer" objects has different properties / definitions.
How can that be handled in C# or in NewtonSoft?
Json structure
"answers": [
{
"tag": {
"id": 803,
"name": "Oplysninger om bestilling af tilstandsrapporten"
},
"option": false,
"answers": [
{
"label": "Vælg",
"value": "Ved hjælp af familie, venner eller bekendte mv",
"show_inline": false
}
],
"question": "Hvordan fandt du den bygningssagkyndige?",
"seller_question_id": 1
}
"answers" here is a property name, not a class name, so all you need to do is define your classes in a way that matches the pattern you've shown. Having two different properties named "answers" doesn't present any particular complication.
class Response
{
public IList<OuterAnswer> answers { get; set; }
}
class OuterAnswer
{
public Tag tag { get; set; }
public bool option { get; set; }
public IList<InnerAnswer> answers { get; set; }
public string question { get; set; }
public long seller_question_id { get; set; }
}
class InnerAnswer
{
public string label { get; set; }
public string value { get; set; }
public bool show_inline { get; set; }
}
I have a class with [DataContract] and [DataMember] attributes on it. I set the Name on the Origin property to be custom variables as that's what the api I'm calling provides. The problem is, that only solves the deserialization of the object. When it comes time to serialize the object, I want to serialize the Origin property as origin.
[DataContract]
public class Request
{
...
[DataMember(Name = "custom variables")]
public Origin Origin { get; set; }
}
For example, I want to deserialize this:
{
...
"custom variables": {
"url": "URL_HERE",
"origin": "ORIGIN_HERE"
}
}
and turn it into this upon serialization:
{
...
"origin": {
"url": "URL_HERE",
"origin": "ORIGIN_HERE"
}
}
How can I do this? Is there any way to do it without writing a custom serializer for all of the properties on the object?
As explained in the official doc:
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#customize-individual-property-names
You must decorate the property with the JsonPropertyName decorator (from the System.Text.Json.Serialization namespace).
In example:
public class WeatherForecastWithPropertyNameAttribute {
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
Serialized/deserialized json:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"Wind": 35
}
After hours of attempts and research, I am asking for your help.
I am calling a public API which returns the same structure except for the datas returned.
For examples, the REST calls which retrieve stations and districts return those two JSON answers :
Stations response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : {
"station": [{
"number": "stationId",
"name": "stationName",
"address": "stationAddress",
"state": "1",
"latitude": "stationLat",
"longitude": "stationLong",
"slotsavailable": "10",
"bikesavailable": "20",
"pos": "0",
"district": "stationDistrict",
"lastupdate": "2016-03-28T11:47:08+02:00"
}, {...}, ...]}
}
Districts response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : { "district": [{"id": "districtId", "name": "districtName"}, {...}, ...] }
}
I am using a .NET 4.5/C# solution with Newtonsoft.Json to execute the call.
I want to make the object, mapped to the client response, generic so the execution of the call will be made as follow :
var result = await client.Execute<Response<ApiResponseDistrict>>(request);
var result = await client.Execute<Response<ApiResponseStation>>(request);
My first attempt was to make a non generic call (create a full object by returned datas) which was a success.
My second attempt was to created a generic object so I made the following classes using the JsonProperty of the library Newtonsoft :
public class ApiResponse<T>
{
[JsonProperty("response")]
public Response<T> Response { get; set; }
}
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
public class ApiResponseDistrict
{
[JsonProperty("district")]
public List<District> Districts { get; set; }
}
public class District
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
At this point, when I am executing the call the object Response is valorized and also its property Status with the value waited but the property Data is never valorized (null).
My third attempt was to continue on the second attempt but using the JsonObject of the Newtonsoft library which it's given (with the same result) :
[JsonObject("district")]
public class ApiResponseDistrict
{
public List<District> Districts { get; set; }
}
As I am new to Newtonsoft, I would like to know if it is possible to use generic classes, as I am trying to do, to mapped the object returned by the call or I have to create a complete object for each "data" returned ?
Thank you for your answer and explanations or clues for me to find the answer !
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
This adds another layer between the data, so a response would look like this:
{
"Status": …,
"Data": {
"ResponseData": {
<The actual type T>
}
}
}
Instead, you want to remove that ResponseData level:
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
So for example, for the JSON above, you would have a StationResponseData class:
public class StationResponseData
{
public List<Station> Stations
{ get; set; }
}
And then you would deserialize the JSON as Response<StationResponseData>. The Station class would then contain those properties for number, name, address, etc.