when do a POST request to a web API the response could be returned in two ways:
{
"Response": {
"StatusCode": 200,
"StatusMessage": "OK",
"Content": {
"auth_hash": "606ca0e7802a070531b4b2fd8ee5fc17b4649a19"
}
}
}
or
{
"Response": {
"StatusCode": 200,
"StatusMessage": "OK",
"Content": {
"document": {
"loja": 5,
"numero": 85099,
"doc": "FS",
"data": "2017-12-13",
"cliente": 0,
"nome": "CONSUMIDOR FINAL",
"liquido": 1.1504,
"total": 1.3,
"anulado": 0,
"emp": 5,
"pago": 1,
"datapag": "2017-12-13",
"tipo": 0,
"pagamento": 1,
"datahora": "2017-12-13 12:51:51",
"deve": 0,
"idcx": 240403,
"mesa": 1001,
"mesaidx": 0,
"lugar": 0
}
}
}
}
How can I deserialize the value "Content" into a C# class object being this values variable?
Best Regards
I assume you know which call can return which response. Then a solution could be to create a generic container, and provide the expected type as generic argument. So the container looks like this:
public class ResponseContainer<TContent>
{
public Response<TContent> Response { get; set; }
}
public class Response<TContent>
{
public int StatusCode { get; set; }
public string StatusMessage { get; set; }
public TContent Content { get; set; }
}
Then you create (or rather, generate) a class per response type:
public class DocumentContent
{
public Document document { get; set; }
}
public class Document
{
public int loja { get; set; }
// ...
public int lugar { get; set; }
}
Then you can deserialize into the type you want, by varying the TContent argument, in this case DocumentContent:
string json = PerformDocumentCall();
var deserialized = JsonConvert.DeserializeObject<ResponseContainer<DocumentContent>>(json);
And for the auth response you pass AuthContent:
public class AuthContent
{
public string auth_hash { get; set; }
}
string json = PerformAuthCall();
var deserialized = JsonConvert.DeserializeObject<ResponseContainer<AuthContent>>(json);
Apart from reading and parsing the raw string and looking for clues, a very easy way is to simply deserialize into one POCO first (using Json.NET or similar). If all values are empty, you try the next type. The order in which you deserialize could be decided out from what's most common or expected in the specific use case.
Or, if you feel slightly more adventurous you could deserialize it into a dynamic, and simply check if response.Content.document exists. Very little code, but not as "strict" as above.
Related
I've simplified the code (below) but I cannot figure out why the Result.Data property is not getting filled; it is always null. I've used jsonlint.com to validate the JSON (both this small sample and the full content). I built a separate project (using How to Deserialize a Complex JSON Object in C# .NET) and it successfully serializes the complex object listed there. But I cannot get this one to work and I'm stumped.
using System.Text.Json;
namespace JsonTest2;
public class Result
{
public string? Total { get; set; }
public string? Limit { get; set; }
public string? Start { get; set; }
protected List<Park>? Data { get; set; }
}
public class Park
{
public string? Id { get; set; }
}
internal class Program
{
var basepath = AppDomain.CurrentDomain.BaseDirectory;
var filepath = basepath.Split("\\bin")[0];
var filename = #$"{filepath}\NPS_response_small.json";
var jsonstr = File.ReadAllText(filename);
var response = JsonSerializer.Deserialize<Result>(jsonstr, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
This is the content of "NPS_response_small.json":
{
"total": "468",
"limit": "50",
"start": "0",
"data": [
{
"id": "77E0D7F0-1942-494A-ACE2-9004D2BDC59E"
},
{
"id": "6DA17C86-088E-4B4D-B862-7C1BD5CF236B"
},
{
"id": "E4C7784E-66A0-4D44-87D0-3E072F5FEF43"
}
]
}
you have to chanbe a protected attribute of property Data to a public. Json deserializer doesnt have any acces to this property
public List<Park>? Data { get; set; }
it would be much easier to use Newtonsoft.Json, but if you need protected for some reason, you can try this ( but I am not sure that it is a full replacement)
public List<Park>? Data { protected get; init ; }
[System.Text.Json.Serialization.JsonConstructor]
public Result (List<Park>? Data, string? Total, string? Limit, string? Start)
{
this.Data=Data;
this.Total=Total;
this.Limit=Limit;
this.Start=Start;
}
This is the JSON im receiving, already filtered. (its coming from the google places autocomplete API)
{
"predictions": [
{
"description": "Frankfurt am Main, Deutschland",
"place_id": "ChIJxZZwR28JvUcRAMawKVBDIgQ",
},
{
"description": "Frankfurt (Oder), Deutschland",
"place_id": "ChIJb_u1AiqYB0cRwDteW0YgIQQ",
},
{
"description": "Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"place_id": "ChIJX3W0JgQYvkcRWBxGlm6csj0",
}
],
"status": "OK"
}
And I need to get this JSON into this format:
{
"success":true,
"message":"OK",
"data":[
{
"description":"Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"id":"ChIJX3W0JgQYvkcRWBxGlm6csj0"
},
{
"description":"Frankfurt Airport (FRA), Frankfurt am Main, Deutschland",
"id":"ChIJeflCVHQLvUcRMfP4IU3YdIo"
},
{
"description":"Frankfurt Marriott Hotel, Hamburger Allee, Frankfurt am Main, Deutschland",
"id":"ChIJdag3xFsJvUcRZtfKqZkzBAM"
}
]
}
I would be very g
So predictions is just renamed to "data", we change rename status to message, move it up and add a success if the http-request that happened earlier was a success or not. This does not seem so hard on the first catch, but I can't seem to find resources to transform or rearrange JSON in C#.
I would be very grateful for any tips or resources, so I can get unstuck on this probably not so difficult task. I should mention I'm fairly new to all of this.
Thank you all in advance!
First create classes thats represent your jsons
public class Prediction
{
public string description { get; set; }
public string place_id { get; set; }
}
public class InputJsonObj
{
public Prediction[] predictions { get; set; }
public string status { get; set; }
}
public class Datum
{
public string description { get; set; }
public string id { get; set; }
}
public class OutPutJsoObj
{
public bool success { get; set; }
public string message { get; set; }
public List<Datum> data { get; set; }
public OutPutJsoObj(){
data = new List<Datum>();
}
}
Then mapped objects (manually or using any of mapping libraries like AutoMapper) and create final json.
using Newtonsoft.Json;
InputJsonObj inputObj = JsonConvert.DeserializeObject<InputJsonObj >(inputJson);
OutPutJsoObj outObj = new OutPutJsoObj ();
foreach(var p in inputObj)
{
outObj.Data.Add(new Datum() { descriptions = p.descriptions , id= p.place_id }
}
string outJson = = JsonConvert.SerializeObject(outObj);
Just parse the origional json and move the data to the new json object
var origJsonObj = JObject.Parse(json);
var fixedJsonObj = new JObject {
new JProperty("success",true),
new JProperty("message",origJsonObj["status"]),
new JProperty("data",origJsonObj["predictions"])
};
it is not clear from your question what should be a success value, but I guess maybe you need this line too
if (fixedJsonObj["message"].ToString() != "OK") fixedJsonObj["success"] = false;
if you just need a fixed json
json = fixedJsonObj.ToString();
or you can create c# class (Data for example) and deserilize
Data result= fixedJsonObj.ToObject<Data>();
I like the answer from #Serge but if you're looking for a strongly typed approach we can model the input and output structure as the same set of classes and the output structure is similar, with the same relationships but only different or additional names this try this:
The process used here is described in this post but effectively we create write-only properties that will receive the data during the deserialization process and will format it into the properties that are expected in the output.
public class ResponseWrapper
{
[JsonProperty("success")]
public bool Success { get;set; }
[JsonProperty("message")]
public string Message { get;set; }
[Obsolete("This field should not be used anymore, please use Message instead")]
public string Status
{
get { return null; }
set
{
Message = value;
Success = value.Equals("OK", StringComparison.OrdinalIgnoreCase);
}
}
[JsonProperty("data")]
public Prediction[] Data { get;set; }
[Obsolete("This field should not be used anymore, please use Data instead")]
public Prediction[] Predictions
{
get { return null; }
set { Data = value; }
}
}
public class Prediction
{
public string description { get; set; }
public string place_id { get; set; }
}
Then you can deserialize and re-serialize with this code:
using Newtonsoft.Json;
...
var input = JsonConvert.DeserializeObject<ResponseWrapper>(input);
var output = JsonConvert.SerializeObject(objs, new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
});
This is a fiddle you can test with: https://dotnetfiddle.net/DsI5Yc
And the output:
{
"success": true,
"message": "OK",
"data": [
{
"description": "Frankfurt am Main, Deutschland",
"place_id": "ChIJxZZwR28JvUcRAMawKVBDIgQ"
},
{
"description": "Frankfurt (Oder), Deutschland",
"place_id": "ChIJb_u1AiqYB0cRwDteW0YgIQQ"
},
{
"description": "Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"place_id": "ChIJX3W0JgQYvkcRWBxGlm6csj0"
}
]
}
If you were going to go to the trouble of writing a converter for the deserialization then I find this solution is a bit simpler. I tend to use this type of solution when exposing additional properties to allow legacy data to map into a the current code base.
keeps the mapping and logic contained within the class
tells developers still writing code against the deprecated structures about the change
You can also augment this and implement a global converter to omit obsolete properties which would give you full backwards compatibility until you update the source to stop sending the legacy structure. This is a fiddle of such a solution: https://dotnetfiddle.net/MYXtGT
Inspired by these posts:
JSON.Net Ignore Property during deserialization
Is there a way to make JavaScriptSerializer ignore properties of a certain generic type?
Exclude property from serialization via custom attribute (json.net)
Json.NET: Conditional Property Serialization
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 trying to de-serialize a weird complex json string but having issues. I am getting an exception:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Response' because the type requires a JSON object (e.g. {"name":"value"})
The Json looks like this
{
"success":true,
"error":null,
"response":{
"responses":[
{
"success":true,
"error":null,
"response":{
"ob":{
"icon":"sunny.png",
"weatherShort":"Sunny"
}
},
"request":"requeststring"
},
{
"success":true,
"error":null,
"response":[
{
"indice":{
"current":{
"dateTimeISO":"2016-08-09T10:00:00-05:00",
"indexENG":"dry"
}
}
}
],
"request":"requeststring"
}
]
}
}
The problem when trying to create a C# class is that inside the responses list there is a Response object and a Response list.
Here is my class structure:
public class Responses
{
public bool success { get; set; }
public object error { get; set; }
public Response response { get; set; }
public List<Response> responses { get; set; }
public string request { get; set; }
}
public class Indice
{
public Current current { get; set; }
}
public class Current
{
public string indexENG { get; set; }
public string dateTimeISO { get; set; }
}
public class Ob
{
public string icon { get; set; }
public string weatherShort { get; set; }
}
public class Response
{
public List<Responses> responses { get; set; }
public Indice indice { get; set; }
public Ob ob { get; set; }
}
public class RootJsonObject
{
public bool success { get; set; }
public object error { get; set; }
public Response response { get; set; }
}
Am I doing something completely wrong here to handle the Responses list with a Response object and a Response list?
In case anyone wants to know, here is how I deserialize it:
RootJsonObject obj = JsonConvert.DeserializeObject<RootJsonObject>(response);
response being the string from a web request.
I am just trying to figure out how to map this strange JSON to a C# class. I've tried quite a few different class structures but seem to get the same exception regardless. I've also tried c# class generators but they don't give a decent output for this particular JSON. Appreciate any input! Thanks!
There is an error in your JSON. Second element in array has square brackets wrapping classic curly brackets, as if response was a collection but it's not. It's expected to be of type Response:
{
"success": true,
"error": null,
"response": [ <<<HERE {
"indice": {
"current": {
"dateTimeISO": "2016-08-09T10:00:00-05:00",
"indexENG": "dry"
}
}
}] <<<HERE,
"request": "requeststring"
}
Final, proper JSON that you should have received would look like this:
{
'success': true,
'error': null,
'response': {
'responses': [{
'success': true,
'error': null,
'response': {
'ob': {
'icon': 'sunny.png',
'weatherShort': 'Sunny'
}
},
'request': 'requeststring'
}, {
'success': true,
'error': null,
'response': {
'indice': {
'current': {
'dateTimeISO': '2016-08-09T10:00:00-05:00',
'indexENG': 'dry'
}
}
},
'request': 'requeststring'
}]
}
}
I've been trying to figure out why some of my tests haven't been working (TDD) and managed to track it down to serialization of a class, but I'm not sure why it's not working. There are two flavours, a simple version and a more complex version, the slightly more complicated one involves having an array of values within the Parameter.Value.
The simple version, I've got a class that can be serailzied using the JavaScriptSerializer (I'm assuming this is how MVC works when it generates JSON). The structure it produces looks like this:
{
"Name": "TestQuery",
"QueryId": 1,
"Parameters": [
{
"Name": "MyString",
"DataType": 0,
"Value": "A String",
"IsArray": false
}],
"Sql": "SELECT * FROM Queries"
}
There are 3 C# classes Query, ParameterCollection (which is a KeyedCollection<String, Parameter>) and a Parameter. All of these are marked up with DataContract/DataMember attributes and serialize via the DataContractSerializer without any problem.
The JavaScriptSerializer however, serializes the object correctly to the JSON above, but upon deserialization I have no Parameters, they just seem to get missed off.
Does anyone have any idea why these fails, and what I might be able to do to fix it?
Why KeyedCollection<String, Parameter>? You have an array, not dictionary, so your JSON should match the following structure:
public class Query
{
public int QueryId { get; set; }
public string Name { get; set; }
public string Sql { get; set; }
public Parameter[] Parameters { get; set; }
}
public class Parameter
{
public string Name { get; set; }
public int DataType { get; set; }
public string Value { get; set; }
public bool IsArray { get; set; }
}
and then you will be able to deserialize it without any problems:
var serializer = new JavaScriptSerializer();
var json = #"
{
""Name"": ""TestQuery"",
""QueryId"": 1,
""Parameters"": [
{
""Name"": ""MyString"",
""DataType"": 0,
""Value"": ""A String"",
""IsArray"": false
}],
""Sql"": ""SELECT * FROM Queries""
}";
var query = serializer.Deserialize<Query>(json);
Also you can get rid of [Data*] attributes from your view models, they are not used by the JavaScriptSerializer class.