c# add null if property in an object does not exist - c#

I have the following response structure which will be serialized following an API call.
public class Details
{
public string firstName { get; set; }
}
public class Result
{
public int maxTry { get; set; }
public int minTry { get; set; }
public List<Details> aggr { get; set; }
public List<string> features { get; set; }
public string zone { get; set; }
}
[Serializable]
public class Root
{
public List<Result> result { get; set; }
}
I have the following array of objects which is an API response.
"result": [
{
“maxTry: 17,
"minTry”: 10,
"details": [
{
“firstName”: “Sam”,
},
{
"firstName”: ”Julio”,
}
],
"aggr": [
“Abc”,
],
"zone": “D3”
},
{
"aggr": [
"Abc",
],
"zone": “C3”
},
{
"aggr": [
"Abc",
],
"zone": “B2”
},
]
}
The problem is even though some objects does not have maxTry and minTry property in the response, when I deserialize the response as follows,
var jobject = JsonConvert.DeserializeObject<Root>(res);
I am still getting the output with maxTry and minTry defaulted to 0. Can this be instead defaulted to null

Sure, make them nullable ints by putting a question mark after int
[JsonProperty("maxTry")] //newtonsoft
public int? MaxTry { get; set; }
[JsonProperty("minTry")]
public int? MinTry { get; set; }
The JsonProperty attribute let's you name your c# property differently to the json, so you can keep c# naming conventions regardless what the json looks like. If you use System.Text.Json it's JsonPropertyName instead
The reason why your ints are coming out as 0 is that ints are value types, which essentially means they must have a value and cannot be null. The default value for an int is 0; even if you never set a value for MaxTry it would have a value of 0
var r = new Result();
Console.WriteLine(r.MaxTry); //prints 0
Nullable ints are harder to work with, because they might be null; you'll need to check if(r.MaxTry.HasValue) if you want to see if it's null or not before you do some things with them. To some degree you might want to reconsider leaving them as ints and treating 0 as them having been absent. It might work quite well for your context for example:
for(int try = 0; try < result.MaxTry; try++)
This won't run if MaxTry is 0.. if that's a problem then you need to decide if maxtry being 0 is ever valid and if it's not, provide some other sensible default if it is 0

Change those properties to nullable int
public int? maxTry { get; set; }
public int? minTry { get; set; }

Related

Nested objects in Swashbuckle auto-generated example

I have a .NET Core API that is documented via Swashbuckle and Swagger. The "example" that is generated for the UI doesn't seem to be including nested objects in the request properly, although they are handled and processed correctly when executing the endpoints.
I have a CreatePaymentRequest class, which is received from the body of the HTTP request, which contains a property of a Notional type. Notional is made up of a decimal value, and a string value.
The generated example looks like this:
{
"tradeId": 0,
"settlementMeans": "SWIFT",
"notional1": {},
"notional1Rate": 0,
"notional2": {},
"notional2Rate": 0,
"paymentReference": "string",
"description": "string"
}
When I would expect it to look like this:
{
"tradeId": 0,
"settlementMeans": "SWIFT",
"notional1": {"Amount": 0, "Currency": "string"},
"notional1Rate": 0,
"notional2": {"Amount": 0, "Currency": "string"},
"notional2Rate": 0,
"paymentReference": "string",
"description": "string"
}
The generated schema also seems to be off, as it doesn't include the properties for Notional:
Though it is listed properly in the "Schemas" section at the bottom:
Here are the classes that make up each object:
public class Request
{
public long TradeId { get; set; }
public SettlementMeans SettlementMeans { get; set; }
public Notional Notional1 { get; set; }
public decimal Notional1Rate { get; set; }
public Notional Notional2 { get; set; }
public decimal Notional2Rate { get; set; }
public string PaymentReference { get; set; } = "";
public string Description { get; set; } = "";
}
public class Notional
{
[JsonContructor]
public Notional(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public Notional()
{
Amount = 0;
Currency = "XXX";
}
public decimal Amount { get; }
public string Currency { get; }
}
Controller method:
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CreatePaymentApi_Response))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(BadRequestResponse))]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<CreatePaymentApi_Response>> PostCreate(CreatePaymentApi_Request request)
{
var command = _mapper.Map<CreatePaymentHandler.Request>(request);
var response = await _mediator.Send(command);
var result = _mapper.Map<CreatePaymentHandler.Response>(response);
return Ok(result);
}
I am setting up SwaggerGen using services.AddSwaggerGen(); in ConfigureServices without any special options.
Having to constantly type out, or copy / paste the JSON for the Notional values is starting to become very repetitive when debugging via the Swashbuckle UI. Is there anything I can do to get the example to generate as I expect it to?
I was eventually able to track this down to the readonly Amount and Currency properties on Notional. I was expecting it to use the constructor marked with JsonConstructor, but apparently, this is only supported when using the newtonsoft serializer, NOT System.Text.Json.
In theory, I should be able to work around this using a custom ISchemaFilter, but I've decided that it isn't worth it just yet to implement one.

Json.Net Deserialize dictionary without property name

I already looked at a lot of other questions with the same problem but never found a definitive solution that actually works for me. I tried using the JsonExtensionData Attribute, that doesn't work though since I can't convert my other data class to an object and it throws the Invalid extension data attribute on 'NAMESPACE'. Member 'Sols' type must implement IDictionary<string, JToken>. error.
My current data model class looks like this
public partial class Mars
{
public Dictionary<string, Sol> Sols { get; set; }
[JsonProperty("sol_keys")]
public List<long> SolKeys { get; set; }
}
public partial class Sol
{
[JsonProperty("AT")]
public At At { get; set; }
[JsonProperty("First_UTC")]
public DateTimeOffset FirstUtc { get; set; }
[JsonProperty("Last_UTC")]
public DateTimeOffset LastUtc { get; set; }
[JsonProperty("Season")]
public string Season { get; set; }
}
public partial class At
{
[JsonProperty("av")]
public double Av { get; set; }
[JsonProperty("ct")]
public long Ct { get; set; }
[JsonProperty("mn")]
public double Mn { get; set; }
[JsonProperty("mx")]
public double Mx { get; set; }
}
The json data looks like this
{
"651":
{
"AT":
{
"av": -61.957,
"ct": 302204,
"mn": -96.733,
"mx": -15.877
},
"First_UTC": "2020-09-25T02:42:14Z",
"Last_UTC": "2020-09-26T03:21:49Z",
"Season": "summer"
},
"652": {
"AT": {
"av": -65.002,
"ct": 278608,
"mn": -96.111,
"mx": -15.653
},
"First_UTC": "2020-09-26T03:21:50Z",
"Last_UTC": "2020-09-27T04:01:24Z",
"Season": "summer"
},
"sol_keys": [
"646",
"647",
"648",
"649",
"650",
"651",
"652"
]
}
I can't really modify the json data since I get it from an api.
I basically just want to select one of the numbers and then get the Sol data of that object.
Any help would be appreciated.
The JSON doesn't fit well with the C# type system. However, you can still use Json.Net to parse it. You just need to introduce some extra steps.
First step is to parse the JSON to a JObject:
var jObject = JsonConvert.DeserializeObject<JObject>(json);
Then you can extract the sol_keys:
var solKeys = jObject.GetValue("sol_keys").ToObject<long[]>();
Now it becomes a bit tricky. If you remove the sol_keys from the JSON (in this case the parsed JSON) it has the structure of a dictionary of Sol objects that you are able to parse:
jObject.Remove("sol_keys");
var mars = jObject.ToObject<Dictionary<long, Sol>>();
Now you have both solKeys and mars parsed from the JSON. Furthermore the solKeys and the keys in the dictionary share the same type (long).

Deserialize JSON array of values in C#

I've got some difficulties with this json script:
{
"insured_agent_flag": "a",
"id": "1",
"agent": {
"fullName": "John Travolta",
"mobileNumberPdf": "+987654321",
"mobileNumber": "",
"identityCard": {
"identityCardExpirationDate": null
},
"secondIdentityCard": {
"identityCardExpirationDate": null
},
"notes": {},
"sign": "ADVANCED"
},
"basicData": {
"personType": "PERSON",
"agreeWithCompleteAnalysis": false,
"investmentInterest": false
},
"nonOfferedProducts": [
"PROD_A",
"PROD_B",
"PROD_C"
]
}
I would like to get some parameters from this script and put it into sql server table.
In order to do that, I used and transformed a C# script shared by https://mycontraption.com:
using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Web.Script.Serialization;
using Microsoft.SqlServer.Dts.Pipeline;
namespace SC_c7e2d8c3918d46a5a07a1b438ddc7642
{
public class BasicData
{
public string agreeWithCompleteAnalysis { get; set; }
public string inOtherSystem { get; set; }
public string investmentInterest { get; set; }
}
public class ParentObject
{
public BasicData BasicData { get; set; }
public int id { get; set; }
public string insured_agent_flag { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
JavaScriptSerializer js = new JavaScriptSerializer();
// Give the input column a variable to make it easier to reference.
BlobColumn combinedColumn = Row.parameterscon;
// Convert from blob to string
string reviewConverted = System.Text.Encoding.ASCII.GetString(combinedColumn.GetBlobData(0, Convert.ToInt32(combinedColumn.Length)));
// Deserialize the string
ParentObject obj = js.Deserialize<ParentObject>(reviewConverted);
var rows = obj.NonOfferedProducts.ToList();
Row.agreeWithCompleteAnalysis = obj.BasicData.agreeWithCompleteAnalysis;
Row.inOtherSystem = obj.BasicData.inOtherSystem;
Row.investmentInterest = obj.BasicData.investmentInterest;
Row.projectionid = obj.id;
Row.insuredagentflag = obj.insured_agent_flag;
//Row.nonOfferedProducts =
}
}
}
For 'standard' objects it works fine, but there is a problem with array "nonOfferedProducts". After compiling I get an error:
„object reference not set to an instance of an object”.
Here are my questions:
1. How should I handle 'nonOfferedProducts' array in C# script?
2. Why do I get foregoing error?
3. Unfortunately there exists a possibility, that json scripts would have some errors, like missing braces. How should I handle that?
Thank you!
Thanks a lot for your answers. According to your comments I'll try to give you more explanations:
1. The json script I have added in this post - it's only small part of whole script. In complete script there is a lot of different parameters. What is more, my C# code should scan about 40.000 json scripts (stored in sql server table in one column). These scripts has got similiar structure - but not the same.
So I thought about C# resolution, that will be searching for the parameters that I need. For json scripts without these parameters the c# code will put nulls to the right output columns.
Here are my output columns:
-agreeWithCompleteAnalysis
-inOtherSystem
-investmentInterest
-projectionId
-insuredAgentFflag
-nonOfferedProducts
I understood, that structure of my classes were wrong - I'll improve that.
But I've got one doubt - is it possible to prepare c# code structure, that will handle only these parameters I need?
And finally, I would like to put the results into my database.
For example if nonOfferedProducts property will have 3 values (not always!), I'd like to send to my database table 3 records (3 different values for nonOfferedProducts column and 3 the same values for the rest columns -agreeWithCompleteAnalysis, inOtherSystem etc).
I hope that will be clear now.
Thanks a lot for your help!
J
Use https://quicktype.io and paste json, it will generate c# model and serializer code.
As I said in my comment, your c# model doesn't match the JSON object.
If the model was made up of various nested objects to better reflect the actual JSON then you'll have more luck:
public class IdentityCard
{
public DateTime? IdentityCardExpirationDate { get; set; }
}
public class Notes
{
//No idea what should be in here...
}
public class BasicData
{
public string PersonType { get; set; }
public bool AgreeWithCompleteAnalysis { get; set; }
public bool InvestmentInterest { get; set; }
}
public class Agent
{
public string FullName { get; set; }
public string MobileNumberPdf { get; set; }
public string MobileNumber { get; set; }
public IdentityCard IdentityCard { get; set; }
public IdentityCard SecondIdentityCard { get; set; }
public Notes Notes { get; set; }
public string Sign { get; set; }
}
//Note: THIS is the actual class that matches the JSON sample given.
public class ParentObject
{
public string insured_agent_flag { get; set; }
public int Id { get; set; }
public Agent Agent { get; set; }
public BasicData BasicData { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
Once the model is correct, then Deserialization works fine for me with the given example (I did this in a unit test, but assuming your string matches your example this should be fine)
//get json
string json = #"
{
""insured_agent_flag"": ""a"",
""id"": ""1"",
""agent"": {
""fullName"": ""John Travolta"",
""mobileNumberPdf"": ""+987654321"",
""mobileNumber"": """",
""identityCard"": {
""identityCardExpirationDate"": null
},
""secondIdentityCard"": {
""identityCardExpirationDate"": null
},
""notes"": {},
""sign"": ""ADVANCED""
},
""basicData"": {
""personType"": ""PERSON"",
""agreeWithCompleteAnalysis"": false,
""investmentInterest"": false
},
""nonOfferedProducts"": [
""PROD_A"",
""PROD_B"",
""PROD_C""
]
}";
var js = new JavaScriptSerializer();
ParentObject obj = js.Deserialize<ParentObject>(json);
//do things...
var rows = obj.NonOfferedProducts.ToList();
Assert.AreEqual(3, rows.Count);
Assert.AreEqual("PROD_A", rows.First());
The asserts pass - This code happily gets the list of strings in the NonOfferedProducts property with the given example.
Obviously if you cannot rely on the consistency of the JSON (either structure or how well-formed it is) then you'll have problems, but that's a different issue.
To answer your question no 2) you are getting the object reference error because the BasicDataClass.nonOfferedProducts is null and you are trying iterate over it , this may be a reason that you are sending the wrong json which JavaScriptSerializer is not able to deserilize.
your 3rd question you can always validate your json with json validators which are there online like https://jsonformatter.org/

GetSet nested property

Having trouble accessing a response back from a rest API service that has nested property.
For example here's the raw REST response:
"count": 5,
"results": [
{
"suggestion": "1 Wonston Road, Southampton, SO16 ...",
"matched": [[ 29, 37 ]],
{
"suggestion": "3 Wonston Road, Southampton, SO16 ...",
"matched": [[ 29, 37 ]],
Suggestion and Match are nested property within results.
The function that gets the response in my code is
IRestResponse<SearchResponse> response = client.Execute<SearchResponse>(request); I've used rest sharp here and the call is actually accurate as I get all the data back in form of a raw response.
I've defined the SearchResponse class as
//same for result, match, suggestion.
private string _count;
public string Count
{
get
{
return _count;
}
set
{
_count = value;
}
}
How can I define nested variables to pass them into SearchResponse.suggestion? Currently the nested properties are stored in results.
public class Result {
public string suggestion { get; set; }
public List<List<int>> matched { get; set; }
public string format { get; set; } }
public class RootObject {
public int count { get; set; }
public List<Result> results { get; set; } }
http://json2csharp.com Did the trick, Thank you

Accessing subfields in json file

Assuming I have a JSON file with the following structure. How I can access the names of the properties in the metadata field.
{
"mappings": {
"basedoc_12kja": {
"properties": {
"created": {
"type": "date",
"format": "dateOptionalTime"
},
"customerID": {
"type": "string"
},
"deleted": {
"type": "boolean"
},
"documentID": {
"type": "string"
},
"id": {
"type": "string"
},
"metadata": {
"properties": {
"Cert": {
"type": "string"
},
"Exp_date": {
"format": "dateOptionalTime"
},
}
}
}
}
}
}
Mappings is an array of documents, each subfield of mappings has a different code. I want to obtain the metadata fields of each document to find out which metadata fields are common between them.
I haven't been able to instantiate this documents.
var response = esReader.GetIndicesMapping();
foreach (var mapping in response.Response.Values)
{
// Parse JSON into dynamic object, convenient!
dynamic results = JObject.Parse(mapping);
List<DocumentType> deserializedObject = JsonConvert.DeserializeObject<List<DocumentType>>(mapping);
}
Exception
{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[DocumentType]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'mappings', line 2, position 14."}
The desire result is to obtain the name of Cert and Exp_date fields
EDIT
public class DocumentType
{
public string Id { set { DocumentID = value; } get { return DocumentID; } }
public string DocumentID { set; get; }
public DateTime Created { set; get; }
.
.
.
public Dictionary<string, object> Metadata { set; get; }
}
The problem here is that your data structure does not match the JSON:
There are no arrays in the JSON. So there is no way you will be able to deserialize that into a C# List.
The "DocumentType" class doesn't match the JSON at all. The class has properties Created, CustomerID, and Deleted which are things like DateTime and string. But the JSON doesn't have those as DateTime or string. They are objects with subproperties named "type" and "format." The property "Metadata" isn't a dictionary: it is an object with a single property named "properties" which should probably be a dictionary.
The case doesn't match.
Don't do that weird thing with Id and DocumentId. The class should match the JSON exactly and literally. No business logic hidden in properties.
The root object has a property called "mappings" so you will need to drill-down before you get to the documents.
Once you successfully get the document, you will need to drill down to the property named "properties" to get to the fields you are interested in.
I suspect there could be multiple documents, and that the "mappings" property contains a list of those documents, where the property names are dynamic and correspond to the name of the document. It is entirely plausible to handle that but not using a deserialization + List approach.
I see 3 approaches here:
Fix the JSON. Not sure if this is possible in your case. If so, start by making mappings hold an array instead of having each document be a property named by the document name.
Fix the deserialization code to match the JSON document. json2csharp did an excellent job so start with that. It just doesn't know that "mappings" is really a Dictionary, not just a thing with a property named "basedoc12_kja."
Don't deserialize it at all. Just query for the metadata. take a look at http://www.newtonsoft.com/json/help/html/QueryingLINQtoJSON.htm which shows several ways to query JSON using JObject properties and LINQ.
Option 1
Example of a slightly cleaned-up JSON if you go that route:
{
"mappings": [
{
"name"" : "basedoc_12kja",
"properties": {
""created": "20150522",
etc.
},
Notice "mappings" is an array and the name became a property of the document. Now you can make a List<> or use JArray. Even better is to get rid of the unused stuff at the top, like this:
[
{
"name" : "basedoc_12kja",
"properties": {
"created"": "20150522",
etc.
},
]
Now it is just an array with no "mappings" at all.
** Option 2 **
Here is code that will do this via deserialization. There are two parts. Step one is to use what json2charp produced. I'll include that here for reference:
public class Created
{
public string type { get; set; }
public string format { get; set; }
}
public class CustomerID
{
public string type { get; set; }
}
public class Deleted
{
public string type { get; set; }
}
public class DocumentID
{
public string type { get; set; }
}
public class Id
{
public string type { get; set; }
}
public class Cert
{
public string type { get; set; }
}
public class ExpDate
{
public string format { get; set; }
}
public class Properties2
{
public Cert Cert { get; set; }
public ExpDate Exp_date { get; set; }
}
public class Metadata
{
public Properties2 properties { get; set; }
}
public class Properties
{
public Created created { get; set; }
public CustomerID customerID { get; set; }
public Deleted deleted { get; set; }
public DocumentID documentID { get; set; }
public Id id { get; set; }
public Metadata metadata { get; set; }
}
public class Basedoc12kja
{
public Properties properties { get; set; }
}
public class Mappings
{
public Basedoc12kja basedoc_12kja { get; set; }
}
public class RootObject
{
public Mappings mappings { get; set; }
}
Then, rename Basedoc12kja to DocumentType, and change RootObject to hold a dictionary. You get this:
public class DocumentType
{
public Properties properties { get; set; }
}
public class RootObject
{
public Dictionary<string, DocumentType> mappings { get; set; }
}
And if you want to get to properties other than just Cert and Exp_date then change Metadata to this:
public class Metadata
{
public Dictionary<string,object> properties { get; set; }
}
Now that can deserialize your document:
JObject results = JObject.Parse(mapping);
RootObject ro = results.ToObject<RootObject>()
You can enumerate through the mappings and get to the properties. They are still messy because of the JSON structure, but you can at least get there.
I hope this helps!
What you have here is a hierarchical dictionary of named properties, where each property can have a type, a format, and possibly a nested dictionary of named child properties -- metadata in your case. You can represent this with the following data model:
[DataContract]
public class PropertyData
{
[DataMember(Name="type", EmitDefaultValue=false)]
public string Type { get; set; }
[DataMember(Name = "format", EmitDefaultValue = false)]
public string Format { get; set; }
[DataMember(Name = "properties", EmitDefaultValue = false)]
public Dictionary<string, PropertyData> Properties { get; set; }
}
[DataContract]
public class Mappings
{
[DataMember(Name = "mappings", EmitDefaultValue = false)]
public Dictionary<string, PropertyData> DocumentMappings { get; set; }
}
(This data model doesn't capture the fact that a given property (probably) can only be a simple type or a complex type with nested properties - but not both. It would seem to suffice for your needs however.)
Then, given the JSON above, you would read it in and convert it to a dictionary of document name to metadata property name as follows:
var mappings = JsonConvert.DeserializeObject<Mappings>(json);
Debug.WriteLine(JsonConvert.SerializeObject(mappings, Formatting.Indented)); // Verify that all was read in.
var metadataNames = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.Properties["metadata"].Properties.Select(p => p.Key).ToList());
Debug.WriteLine(JsonConvert.SerializeObject(metadataNames, Formatting.Indented)); // Inspect the resulting mapping table.
And the result is the dictionary of metadata names you want:
{
"basedoc_12kja": [
"Cert",
"Exp_date"
]
}
If you are concerned that the nested metadata might be missing sometimes and so generate NullReferenceExceptions in the query above, you can add null checks as follows:
// Extension methods to query or walk through nested properties, avoiding null reference exceptions when properties are missing
public static class PropertyDataExtensions
{
public static IEnumerable<KeyValuePair<string, PropertyData>> GetProperties(this PropertyData data)
{
if (data == null || data.Properties == null)
return Enumerable.Empty<KeyValuePair<string, PropertyData>>();
return data.Properties;
}
public static PropertyData GetProperty(this PropertyData data, string name)
{
if (data == null || data.Properties == null)
return null;
PropertyData child;
if (!data.Properties.TryGetValue(name, out child))
return null;
return child;
}
}
And then:
var metadataNamesSafe = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.GetProperty("metadata").GetProperties().Select(p => p.Key).ToList());

Categories