C# Deserialize JSON with Key Name Starting with String - c#

I have some JSON that looks like this:
{
"fs_Unique_Form_Name": {
"title": "Title Text",
"description": "This is the description.",
"code": "123"
},
"stackId": 1,
"stateId": 1,
"timeStamp": "2020-11-04:10.30.48"
}
I am using Newtonsoft.Json to try and deserialize it to a model object, but am having a hard time with the dynamic keys; I'd like to make that property generic if possible since the inner object is always the same.
Here are the models I am trying to use:
public class FormDetail {
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "code")]
public string Code { get; set; }
}
public class FormResponse {
[JsonProperty(PropertyName = "NEED TO FIGURE THIS OUT")]
public FormDetail FormDetail { get; set; }
[JsonProperty(PropertyName = "stackId")]
public int StackId { get; set; }
[JsonProperty(PropertyName = "stateId")]
public int StateId { get; set; }
[JsonProperty(PropertyName = "timeStamp")]
public string TimeStamp { get; set; }
}
I would like to get the whole JSON deserialized into the FormResponse object, but am having difficulty because the fs_Unique_Form_Name key is dynamic after the fs_ portion, but the keys (title, description, code) in that object are static.
Is there a way for me to do something where I can deserialize it to the FormDetail property when the JSON key starts with fs_?

You can do this by making a custom JsonConverter for your FormResponse class. Below is the code you would need for the converter:
public class FormResponseConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(FormResponse);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON into a JObject so we can inspect it.
JObject obj = JObject.Load(reader);
// Populate all the known properties on a new instance of FormResponse.
FormResponse response = new FormResponse();
serializer.Populate(obj.CreateReader(), response);
// Now find the first property in the JObject whose name starts with "fs_".
// If there is one, use it to populate the FormDetail on the response.
JProperty prop = obj.Properties().FirstOrDefault(p => p.Name.StartsWith("fs_"));
response.FormDetail = prop != null ? prop.Value.ToObject<FormDetail>(serializer) : null;
return response;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, add a [JsonConverter] attribute to your FormResponse class as shown below. You can remove the [JsonProperty] attribute from the FormDetail property as it is not needed.
[JsonConverter(typeof(FormResponseConverter))]
public class FormResponse
{
public FormDetail FormDetail { get; set; }
[JsonProperty(PropertyName = "stackId")]
public int StackId { get; set; }
[JsonProperty(PropertyName = "stateId")]
public int StateId { get; set; }
[JsonProperty(PropertyName = "timeStamp")]
public string TimeStamp { get; set; }
}
Then you can deserialize as you normally would:
var response = JsonConvert.DeserializeObject<FormResponse>(json);
Here is a working demo: https://dotnetfiddle.net/jHyRcK

There are many ways to do it.
For example, let's create a custom reader.
class CustomJsonReader : JsonTextReader
{
public CustomJsonReader(TextReader reader) : base(reader) { }
public override object Value
{
get
{
if (base.TokenType == JsonToken.PropertyName &&
base.Value.ToString().StartsWith("fs_"))
return "FormDetail";
return base.Value;
}
}
}
Use it like this
FormResponse response;
var serializer = JsonSerializer.CreateDefault();
using (var streamReader = new StreamReader("test.json"))
using (var jsonReader = new CustomJsonReader(streamReader))
{
response = serializer.Deserialize<FormResponse>(jsonReader);
}
In this case, the FormDetail property does not need the JsonProperty attribute.

Related

ReadAsAsync JSON with non-static datamodels into model

The following is a model based on the response from an API (using sample data):
public class SchoolInfoModel
{
public string Name { get; set; }
public string Website { get; set; }
public Address Address { get; set; }
public List<SchoolTypeModel> SchoolTypes { get; set; }
}
The SchoolTypeModel is where I'm stuck. The SchoolType will return a list but the content of the list might contain one other model or two other models or more. Basically a list of a number of different models. But I do not know in advance which models I receive. How do I map these?
Examples of "SchoolType" models I can receive:
public class HighSchoolModel
{
public string Type { get; set; }
public string SubName { get; set; }
public bool BA { get; set; }
public bool CP { get; set; }
public bool HU { get; set; }
public bool MN { get; set; }
public bool TI { get; set; }
}
public class SpecialPurposeSchoolModel
{
public string Type { get; set; }
public string SubName { get; set; }
public bool AH { get; set; }
}
I have a total of about 10 different types of school.
Here's a way you can achieve this with JSON.Net (which means it will work in .NET Framework 4.8). You can create a custom converter to figure out what properties exist in this JSON and use that to determine which type to deserialise to. For example:
public class SchoolTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(SchoolTypeModel);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
if (obj.ContainsKey("AH"))
{
return obj.ToObject<SpecialPurposeSchoolModel>(serializer);
}
if (obj.ContainsKey("BA"))
{
return obj.ToObject<HighSchoolModel>(serializer);
}
// We have no idea what this is, so panic
throw new Exception("No idea what to do with this value!");
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
// We are only using this to read JSON
throw new NotImplementedException();
}
}
And to read the JSON:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new SchoolTypeConverter());
var result = JsonConvert.DeserializeObject<SchoolInfoModel>(yourJsonString,
settings); // Pass in the settings object we created above

how Convert a json string value to long when mapping json results to an object

I have the following object
public class Error
{
public long id { get; set; }
public string code { get; set; }
public string message { get; set; }
public long qty{ get; set; }
}
public class RootObject
{
public List<Error> error { get; set; }
}
my Json result is like this
{"error":[{"id":"15006","code":"Error CODE","message":"Error Message","qty":""}]}
and I map the json string to my object like this
RootObject obj =JsonConvert.DeserializeObject<RootObject>(jsonString);
My question is the id and qty fields. The json result wraps the id and qty in quotes making it a string but I want this to be a long datatype. I need this as I'm using a library to export this data to Excel and I'm having issues as is exporting it as text instead of numeric. Also the qty is empty when there is no value instead of 0. I tried long? and int? but while this works, I prefer to default to 0 if the result is empty.
I can't change the json result. I have tried converting the string to int64 in the get{} but I'm getting errors.
public long id {
get
{
var idCode = Convert.ToInt64(this.id);
return idCode;
}
}
Any ideas how I can do this? Keep in mind that I have many other fields like this.
I think you can find the answer to your question here
Convert long number as string in the serialization
with some tweaking
public class Error
{
[JsonIgnore]
public long Id{ get; set; }
[JsonIgnore]
public long qty{ get; set; }
[JsonProperty("id")]
public string IdAsString
{
get { return id.ToString(); }
set { id = long.Parse(value); }
}
[JsonProperty("qty")]
public string QtyAsString
{
get { return qty.ToString(); }
set { qty = long.Parse(value); }
}
}
In such cases you can not use automatic properties and you must use the attribute
public class Error
{
private long? _id;
public long id
{
get
{
return Convert.ToInt64(_id);
}
set
{
_id = value;
if (!_id.HasValue)
_id = 0;
}
}
public string code { get; set; }
public string message { get; set; }
public long qty { get; set; }
}
Deserialization.
You need nothing : it will convert with out issue string to int, flaot, decimal etc.
Even if it's a string in the Json : "id":"15006"
public partial class Error
{
[JsonProperty("id")]
public long Id { get; set; }
}
var result =JsonConvert.DeserializeObject<RootObject>(input);
Live demo https://dotnetfiddle.net/qliQ8m
Serialization .
You can either use a DTO class with the correct type or custom converter:
internal class ParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
long l;
if (Int64.TryParse(value, out l))
{
return l;
}
throw new Exception("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (long)untypedValue;
serializer.Serialize(writer, value.ToString());
return;
}
public static readonly ParseStringConverter Singleton = new ParseStringConverter();
}
And you decorate your property wi the custom converter: [JsonConverter(typeof(ParseStringConverter))]
public partial class Error
{
[JsonProperty("id")]
[JsonConverter(typeof(ParseStringConverter))]
public long Id { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("qty")]
public string Qty { get; set; }
}
Default and Null:
You can use JsonSerializer option :
DefaultValueHandling.Ignore
NullValueHandling.Ignore,
On serialization simply add the parameter like :
string jsonString =
JsonConvert.SerializeObject(
myObject,
new JsonSerializerSettings {
DefaultValueHandling = DefaultValueHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
}
);

Deserialize Object List Json

I'm new in C# and I want to get a JSON from my app in heroku. Before I did it in javascript with no problems but I'm triying to do it in C# now.
My code is:
namespace FirstApp
{
public class AOA
{
public Data data { get; set; }
public static void Main()
{
var json = string.Empty;
var aoa = new List<AOA>();
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(#"http://beautyglam.herokuapp.com/aoa/eyeliner?page=1");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
json = reader.ReadToEnd();
aoa = JsonConvert.DeserializeObject<List<AOA>>(json);
Console.WriteLine("DATOS");
Console.WriteLine(json);
Console.WriteLine(aoa[0].data);
}
catch(Exception e)
{
Console.WriteLine("Errorrr: " + e);
}
}
}
public class Data
{
public string category { get; set; }
public int pages { get; set; }
public string name { get; set; }
public int? price { get; set; }
public string id { get; set; }
public string img { get; set; }
}
}
The returned json is something like:
[{"category":"AOA Eye Liner","pages":0},{"name":"AOA Starlet Eyeliner- Spotlight","price":150,"id":"751612657678","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-164-MAIN_large.jpg?v=1539192189"},{"name":"AOA Starlet Eyeliner- Hollywood","price":150,"id":"751612592142","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-166-MAIN_large.jpg?v=1539192156"},{"name":"AOA Starlet Eyeliner- Fame","price":150,"id":"751612559374","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-169-MAIN_large.jpg?v=1539192087"},{"name":"AOA Starlet Eyeliner- Superstar","price":150,"id":"751612526606","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-167-MAIN_large.jpg?v=1539192203"},{"name":"AOA Starlet Eyeliner- Paparazzi","price":150,"id":"751612461070","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-165-MAIN_large.jpg?v=1539192338"},{"name":"AOA Starlet Eyeliner- Golden Girl","price":150,"id":"751612329998","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-168-MAIN_large.jpg?v=1539192127"},{"name":"AOA Wonder Liquid Liner - Black","price":150,"id":"194917793806","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-036-1_large.jpg?v=1522944978"},{"name":"AOA Wonder Liquid Liner - Dark Brown","price":150,"id":"194917335054","img":"//cdn.shopify.com/s/files/1/0882/6874/products/AOA-037-1_large.jpg?v=1522944879"}]
I want to use it like in Javascript, for example:
aoa[0].name;
Your class representation of your json structure is not valid for your example. Also, your json structure needs to be parsed with some special deserializer because it is not contains consist values.
You have an array of json object but they are not have same type.
You need 3 classes which one represents category and pages, one represents name,price,id,img and one for the root which containts those two classes.
Here is what it should look like:
class RootObj {
public Category category { get; set; }
public List<Detail> details { get; set; }
}
class Category {
public string category { get; set; }
public int pages { get; set; }
}
class Detail {
public string name { get; set; }
public int price { get; set; }
public string id { get; set; }
public string img { get; set; }
}
Here is custom deserializer:
class CustomObjectConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
RootObj result = new RootObj();
result.category = array[0].ToObject<Category>();
array.RemoveAt(0);
result.details = array.ToObject<List<Detail>>();
return result;
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(RootObj));
}
}
Main :
string jsonText = File.ReadAllText("json.txt");
var deserializer = new JsonSerializerSettings();
deserializer.Converters.Add(new CustomObjectConverter<RootObj>());
var result = JsonConvert.DeserializeObject<RootObj>(jsonText, deserializer);
Now you can access the properties as :
Console.WriteLine(result.category.category);
Output is :
AOA Eye Liner
You can customize it according to your use cases.

Deserialize JSON Array with No Member Names to C# Object

I am in search a method that deserializes an array (without member names) to a C# object. Working and expected examples are provided and I have seen many similar posts but not quite what I am looking for hence why felt to ask out for some assistance.
Do I need to take the approach of implementing custom deserialization or am I missing out something already existing?
// Deserialize Works This Way
public class Order
{
public string orderNo { get; set; }
public string customerNo { get; set; }
public List<List<double>> items { get; set; }
}
// Expected Covert Type.
public class OrderExpected
{
public string orderNo { get; set; }
public string customerNo { get; set; }
public List<OrderItem> items { get; set; }
}
public class OrderItem
{
public int itemId { get; set; }
public decimal price { get; set; }
public decimal quantity { get; set; }
}
Code I have tried and what I would like to get working:
var json = "{\"orderNo\":\"AO1234\",\"customerNo\":\"C0129999\",\"items\":[[255, 1.65, 20951.60],[266, 1.80, 20000.00],[277, 1.90,0.50]]}";
// Works OK, but ins't what I am after
var order = JsonConvert.DeserializeObject<Order>(json);
// I'd like to get some help to get this approch working.
var orderexpected = JsonConvert.DeserializeObject<OrderExpected>(json);
Further information on items array:
The items array is going to consist of arrays which have fixed length of 3 and values represent itemId, price and quantity respectively.
P.S. I am consuming an API which is out of my control.
this can help you..
public class OrderExpected
{
public string orderNo { get; set; }
public string customerNo { get; set; }
public List<OrderItem> items { get; set; }
}
[JsonConverter(typeof(OrderItemConverter))]
public class OrderItem
{
public int itemId { get; set; }
public decimal price { get; set; }
public decimal quantity { get; set; }
}
public class OrderItemConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.Name.Equals("OrderItem");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
return new OrderItem {
itemId = array[0].ToObject<int>(),
price = array[1].ToObject<decimal>(),
quantity = array[2].ToObject<decimal>()
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var orderItem = value as OrderItem;
JArray arra = new JArray();
arra.Add(orderItem.itemId);
arra.Add(orderItem.price);
arra.Add(orderItem.quantity);
arra.WriteTo(writer);
}
}
using..
string jsonString = "{\"orderNo\":\"AO1234\",\"customerNo\":\"C0129999\",\"items\":[[255, 1.65, 20951.60],[266, 1.80, 20000.00],[277, 1.90,0.50]]}";
var objectResult = JsonConvert.DeserializeObject<OrderExpected>(jsonString);
var serializationResult = JsonConvert.SerializeObject(objectResult);
Console.WriteLine(serializationResult);
// output : {"orderNo":"AO1234","customerNo":"C0129999","items":[[255,1.65,20951.6],[266,1.8,20000.0],[277,1.9,0.5]]}
You can use custom JsonConverter for specified property by using attribute: JsonConverter(typeof(YourCustomConverter))
In your case simple examle should looks like this:
public class OrderExpected
{
public string OrderNo { get; set; }
public string CustomerNo { get; set; }
[JsonConverter(typeof(OrderItemConverter))]
public List<OrderItem> Items { get; set; }
}
public class OrderItemConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jArray = JArray.Load(reader);
var result = new List<OrderItem>();
foreach (var arrayItem in jArray)
{
var innerJArray = arrayItem as JArray;
if(innerJArray?.Count != 3)
continue;
result.Add(new OrderItem
{
ItemId = (int) innerJArray[0],
Price = (decimal)innerJArray[1],
Quantity = (decimal)innerJArray[2]
});
}
return result;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
And deserialize your json as usual.
var obj = JsonConvert.DeserializeObject<OrderExpected>(json);
Alright, I agree with Levent. I just want to develop this answer!
in Json.Net side use this attribute [JsonExtensionData] when Json string contains properties without property names
[Serializable]
[JsonObject]
public class Price
{
[JsonExtensionData]
public IDictionary<string, JToken> Values { get; set; }
[JsonProperty("vendor")]
public Dictionary<string, string> vendor { get; set; }
}

newtonsoft.json deserializing logic

I'm trying to parse a json string with for example [1,2,3] to an array during deserializing.
This is my json data:
[
{
"id": "1",
"district": "1",
"lon": "4.420650000000000000",
"lat": "51.21782000000000000",
"bikes": "19",
"slots": "14",
"zip": "2018",
"address": "Koningin Astridplein",
"addressNumber": null,
"nearbyStations": "3,4,5,24",
"status": "OPN",
"name": "001- Centraal Station - Astrid"
}
]
And this is my c# currently mapping to a regular string, which i would like to be an array of integers.
var AvailabilityMap = new[] { new Station() };
var data = JsonConvert.DeserializeAnonymousType(json, AvailabilityMap);
public class Station
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
public string NearbyStations { get; set; }
public string Status { get; set; }
public string Name { get; set; }
}
I have found no way so far to do this in a proper way, without looping trough my current array once more..
Create a custom converter. Something like this:
public class StringToIntEnumerable : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override bool CanWrite
{
get
{
return false; // we'll stick to read-only. If you want to be able
// to write it isn't that much harder to do.
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Note: I've skipped over a lot of error checking and trapping here
// but you might want to add some
var str = reader.Value.ToString();
return str.Split(',').Select(s => int.Parse(s));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Now change you class to use the converter by using the JsonConverterAttribute:
public class Station
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
[JsonConverter(typeof(StringToIntEnumerable))]
public IEnumerable<int> NearbyStations { get; set; } // or List<int> or int[] if
// you prefer, just make
// sure the convert returns
// the same type
public string Status { get; set; }
public string Name { get; set; }
}
And now to deserialize:
var stations = JsonConvert.DeserializeObject<List<Station>>(json);
Here is a working fiddle that uses a custom JsonConverter.
What we're doing is converting your CSV values into a proper JSON array before we convert the entire JSON string into a Station object.
Custom JsonConverter
ReadJson reads through the JSON string. First, it loads the JSON into a JObject. Next, it gets the nearbyStations property and changes it from a simple CSV string into a JavaScript array. It does this by wrapping the CSV within square brackets. Finally, we use the JsonSerializer to populate our target object and return it.
CanWrite is set to false, because this JsonConverter is only allowed to read JSON not write to JSON. As a result, we don't need to implement WriteJson. The CanConvert tests to make sure that the target object is a Station.
public class StationConverter : JsonConverter
{
public override object ReadJson(
JsonReader r, Type t, object v, JsonSerializer s)
{
JObject jObject = JObject.Load(r);
var prop = jObject.Property("nearbyStations");
var wrapped = string.Format("[{0}]", prop.Value);
JArray jsonArray = JArray.Parse(wrapped);
prop.Value = jsonArray;
var target = new Station();
s.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter w, object v, JsonSerializer s)
{
throw new NotImplementedException("Unnecessary: CanWrite is false.");
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Station);
}
}
Change the Station Class to have an int[]
For the above JsonConverter to to work, change your NearbyStations property to be an int[].
public int[] NearbyStations
{
get;
set;
}
Usage Example with Live Fiddle
Here is an example of how to use it:
var AvailabilityMap =
JsonConvert.DeserializeObject<Station[]>(json, new StationConverter());
Console.WriteLine(AvailabilityMap[0].NearbyStations[0]);
The right answer is to have properly formatted input. In this case:
"nearbyStations": ["3","4","5","24"]
But I was faced with a similar situation in which the data could not be updated, so a solution had to be found. The easiest is to make a getter/setter that won't be touched by the serializer/deserializer. Unfortunately you can't ignore public properties with this serializer out of the box. So you have to do some clever work-arounds like reading into a DTO and using a business object for actual operations.
public class StationDTO
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
public string NearbyStations { get; set; }
public string Status { get; set; }
public string Name { get; set; }
}
...
public class Station : StationDTO
{
public List<string> NearbyStationsList
{
get
{
return NearbyStations.Split(',');
}
set
{
NearbyStations = string.Join(",", value);
}
}
}
More information: Newtonsoft ignore attributes?

Categories