Want to pass empty string in datetime field in c# - c#

I am passing empty value in json payload in datetime field. Below is json payload example.
Here in dateOf in Loans2021 I am passing ""
{
"isCompany": null,
"Loans2020": {
"dateOf": "2022-12-31T19:00:00.000Z",
"Amount": 1000,
},
"Loans2021": {
"Amount": 0,
"dateOf": ""
}
}
I am getting error as per below.
"errors": {
"request": [
"The request field is required."
],
"$.Loans2021.dateOn": [
"The JSON value could not be converted to System.Nullable`1[System.DateTime]. Path:.."
]
I want to pass empty string in datetime field so any idea how to do this.
I am using C#, .net core in web api

You can't pass the dateOf in this "" format instead of this pass the dateOf as null.
Try like this
{
"isCompany": null,
"Loans2020": {
"dateOf": "2022-12-31T19:00:00.000Z",
"Amount": 1000,
},
"Loans2021": {
"Amount": 0,
"dateOf": null
}
But Before this set your dateOf property as nullable in your entity class
public DateTime? Date { get; set; }

You're trying to pass a string to an object of type DateTime.
Try to implement a custom json converter.
In your C# property, add the declaration:
[JsonPropertyName("dateOf")]
[JsonConverter(typeof(stringToDateTimeConverter))]
public DateTime? date;
Here is an example:
public sealed class StringToDateTimeConverter : JsonConverter<double>
{
public override double Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
DateTime.TryParse(reader.GetString(),out DateTime value);
return value;
}
public override void Write(
Utf8JsonWriter writer,
double value,
JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}

Related

C# JSON deserialize a file into object list fails with convert String to Collection error

I've got a little project for school and we are dealing with some json stuff here.
I can write down a object List into JSON and this file is a valid JSON format.
No once I load the very same file I do receive an error complaining about not beeing able to convert. Here is the message:
Newtonsoft.Json.JsonSerializationException
HResult=0x80131500
Message=Error converting value "[
{
"ID": 500455154,
"Title": "GameOne",
"MinAge": 14,
"Price": 12.8,
"State": "New",
"Players": 4
},
{
"ID": 860100321,
"Title": "Gametwo",
"MinAge": 14,
"Price": 12.8,
"State": "New",
"Players": 4
},
{
"ID": 358239485,
"Title": "Gamethree",
"MinAge": 14,
"Price": 12.8,
"State": "New",
"Players": 4
}
]" to type 'System.Collections.Generic.List`1[Ludothek.Game]'. Path '', line 1, position 513.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Ludothek.DATA.loadData_Games() in epos\Ludothek\Ludothek\DATA.cs:line 71
at Ludothek.Program.Main(String[] args) in \r\Program.cs:line 16
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List`1[Ludothek.Game].
This is my function to load this file and push it into an existing List of game:
public List<Game> loadData_Games()
{
var Gl = new List<Game>();
string[] files = Directory.GetFiles(folder, "games.json");
foreach (var file in files)
using (StreamReader sr = new StreamReader(file))
{
using (JsonReader jsr = new JsonTextReader(sr)) {
var Serializer = new JsonSerializer();
Gl = (List<Game>)Serializer.Deserialize(jsr, typeof(List<Game>));
// above is where it hits the fan...
}
}
return Gl;
}
And of course here is the game class:
public class Game : AbstGame
{
[JsonProperty("ID")]
public override int ID { get ; }
[JsonProperty("Title")]
public override string Title { get ; set ; }
[JsonProperty("MinAge")]
public override int MinAge { get ; set ; }
[JsonProperty("Price")]
public override double Price { get; set ; }
[JsonProperty("State")]
public override string State { get; set ; }
[JsonProperty("Players")]
public int Players { get; set; }
public Game(string title, int minAge, double price,string state, int players)
{
Random rnd = new Random();
ID = rnd.Next(1, 999999999);
Title = title;
MinAge = minAge;
Price = price;
State = state;
Players = players;
}
and this is how I write it down:
public void backUp_Games(List<Game> games)
{
string filename = "games";
string jsonGames = JsonConvert.SerializeObject(games,Formatting.Indented);
backUp(filename, jsonGames);
}
public void backUp(string fileName ,string json)
{
string filename = #"C:\.....\Desktop\JsonTest\"+fileName+".json"; // not the actual path ;)
File.WriteAllText(filename, JsonConvert.SerializeObject(json,Formatting.Indented));
}
I really travelled the internet to find a solution to this problem and tried already several other approaches. I cant spot the issue. Does anyone else see it and could give me a hint?
What I do in code is like generate a List of games with some attributes. Once the window gets closed, I will push that games object list into json and safe it, once we open it it reloads.
So it has to get the object list out of json.
Many thanks in advance!
and this file is a valid JSON format
Yes, it's valid JSON but not what you expect. You write a file with only one JSON string.
Simplified example:
"foo"
This is valid json. It's just a string. You do the same but the string content looks like JSON.
"{\"foo\": \"bar\"}"
This is still just one string no JSON object.
That's why you get the error: Could not cast or convert from System.String to System.Collections.Generic.List`1[Ludothek.Game].
Here's what you do...
string jsonGames = JsonConvert.SerializeObject(games,Formatting.Indented);
// string jsonGames is the JSON you want but then you do...
File.WriteAllText(filename, JsonConvert.SerializeObject(json,Formatting.Indented));
So, you do the JSON encoding two times and JSON encoding a JSON string will result in a string as described above.
Solution: Encode only once.

Can't set TimeSpan property from JSON source C#

I have been trying to read a JSON file and deserialize it to a class object. The problem is that I am storing a TimeSpan? property of the class as string in the JSON file which doesn't get deserialized and remains null. Please find the code for reference below:
Interface:
public interface IAirport
{
.
.
.
TimeSpan? TimeOffset { get; set; }
.
.
.
}
Class :
public class Airport:IAirport{
.
.
.
private TimeSpan? _offsetTime;
public TimeSpan? OffssetTime
{
get{return _offsetTime;}
set{SetProperty<TimeSpan>(ref _offsetTime, value);}
}
.
.
.
}
This is the way I deserialize :
private static T ConvertJsonStreamToObject<T>(StreamReader fileStream)
{
JsonSerializer serializer = new JsonSerializer();
return (T)serializer.Deserialize(fileStream, typeof(T));
}
and lastly the JSON :
{
"AirportDesignation": "ZZU",
"EffectiveDate": "1901-01-01 00:00:00.0000000",
"Description": "Mzuzu",
"DescriptionShort": "Mzuzu",
"EffectiveStatus": "A",
"Country": "MWI",
"State": " ",
"LatitudeDegrees": 11,
"LatitudeMinutes": 27,
"LatitudeSeconds": 0,
"LatitudeCoordinates": "S",
"LongitudeDegrees": 34,
"LongitudeMinutes": 1,
"LongitudeSeconds": 0,
"LongitudeCoordinates": "E",
"AirportType": "A",
"TimeOffset": "00:00:00",
"IsTimeOffsetPositive": true
}
I tried the following way to set the property and use TimeSpan.Parse() to parse the string to TimeSpan but none of them worked :
private string _timeOffset;
public TimeSpan? TimeOffset
{
get { return TimeSpan.Parse(_timeOffset); }
set { _timeOffset = value.ToString(); }
}
I am out of ideas to try, any help would be appreciated.
UPDATE:
I tried #Caius Jard suggestion, getting the System.ArgumentNullException
Updated the property as below:
private string _timeOffset;
[Required]
public TimeSpan? TimeOffset
{
get { return TimeSpan.Parse(_timeOffset); }
set { SetProperty<string>(ref _timeOffset, value.Value.ToString()); }
}
Found the culprit finally, It was an attribute causing the problem. I had an interface as the base for the Airport class and it required the TimeOffset to be a [DataMember] otherwise the property isn't identified as the data member of the class.
Adding the [DataMember] attribute now sets the value.

How can I parse json to a C# object (Class)?

I have JSON data that I need to parse from C# object.
this is JSON Example.
{
"types":
[
[
"tour_type",
[
["groups",1],
["individual",2]
]
]
]
}
Here are my C# classes that are meant to contain that data:
using System;
using Newtonsoft.Json;
namespace JsonDeserializationTest
{
[JsonProperty("types")]
public class Types
{
[JsonProperty]
public List<Type> Values {get;set;}
}
public class Type
{
[JsonProperty]
public string Key {get;set;}
[JsonProperty]
public List<Dictionary<string, int>> Values { get; set; }
}
}
It's not working now.
How can I fix it?
Use the JsonSerializer (System.Text.Json) object.
Code:
YourClass obj = JsonSerializer.Deserialize<YourClass>(jsonString);
Your json has a list of list of the object... but you are declaring only List of the object.
public class Types
{
[JsonProperty("types")]
public List<List<object>> Values { get; set; }
// ------ UPDATE: This can only be list of list of 'object' ------- \\
}
Also, you are using the JsonProperty on the class, which is not where that normally goes. You want to use that on the property of the class.
UPDATE:
You cannot use List<List<Type>> for the json you are getting, it can only be List<List<object>>. You have to use object because it can either be a string or a List<List<string>>. After you update your Types class, you can successfully deserialize the json above.
var obj = JsonConvert.DeserializeObject<Types>(json);
and based on your json definition, you can access tour_type by using the following code
types.Values.First()[0].ToString()
// output: tour_type
List<List<string>> data = JsonConvert.DeserializeObject<List<List<string>>>(types.Values.First()[1].ToString())
// data[0]
[0]: "groups"
[1]: "1"
// data[1]
[0]: "individual"
[1]: "2"
Since both of the items in the types are objects, you will either have to convert them to string or a list of list of strings or whatever object they actually are.
The JSON payload in the provided example is formatted quite strangely, especially since it contains seemingly unnecessary array nesting. A payload like this usually includes more nested objects (rather than a bunch of nested arrays). Additionally, it has a list of (string, int) pairs, which is semantically very similar to a Dictionary<string, int>, but the payload doesn't lend itself to that. It would be helpful to know where it is coming from (what context) to understand how it might change.
The example JSON brings up a few questions (that you may want to ask yourself):
Can the "types" array contain multiple entries (at its immediate nesting)?
Can the "tour_type" key name appear after the array of string, int pairs? Is it possible for an entry where no such name exists?
What other elements can exist in the arrays within "tour_type"?
Is it guaranteed that the most nested array will contain just a single (string, int) pair?
Similarly, it is hard to understand what the example C# class is trying to encapsulate. Is List<Dictionary<string, int>> necessary?
All that said, here's a solution using the built-in System.Text.Json library, that could work for you. You could write something similar using Newtonsoft.Json, if necessary. The solution assumes:
We can't change the JSON payload (and that the third party API response will always returns something that is structurally similar to the example)
We can only make minimal changes to the C# class object provided in the example
The solution creates and a JsonConverter<T> that uses the low-level Utf8JsonReader to manually parse and create the custom object. This is required since nested "[" are being used to delineate what should be objects rather than "{". The converter is then registered by annotating the class with the attribute. Now, simply call JsonSerializer.Deserialize, passing in the JSON payload.
public class Tours
{
[JsonPropertyName("types")]
public List<UserType> Types { get; set; }
}
// Annotate the type to register the converter to use
[JsonConverter(typeof(CustomUserTypeConverter))]
public class UserType
{
public string Key { get; set; }
public Dictionary<string, int> Values { get; set; }
}
// This will use the low-level reader to build up the UserType
public class CustomUserTypeConverter : JsonConverter<UserType>
{
// Extra structural validation was done for invalid/incomplete JSON
// which might be too strict or incorrect and hence might require adjustments.
public override UserType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var result = new UserType();
if (!reader.Read())
{
throw new JsonException("Incomplete JSON.");
}
if (reader.TokenType != JsonTokenType.EndArray)
{
result.Key = reader.GetString();
ReadAndValidate(ref reader, JsonTokenType.StartArray);
int depthSnapshot = reader.CurrentDepth;
var values = new Dictionary<string, int>();
do
{
reader.Read();
if (reader.TokenType != JsonTokenType.StartArray && reader.TokenType != JsonTokenType.EndArray)
{
throw new JsonException($"Invalid JSON payload. Expected Start or End Array. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}.");
}
if (reader.CurrentDepth <= depthSnapshot)
{
break;
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
{
string key = reader.GetString();
reader.Read();
int value = reader.GetInt32();
values.Add(key, value);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
}
} while (true);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
result.Values = values;
}
return result;
}
private void ReadAndValidate(ref Utf8JsonReader reader, JsonTokenType expectedTokenType)
{
bool readNext = reader.Read();
if (!readNext || reader.TokenType != expectedTokenType)
{
string message = readNext ?
$"Invalid JSON payload. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}, Expected: {expectedTokenType}" :
$"Incomplete JSON. Expected: {expectedTokenType}";
throw new JsonException(message);
}
}
// Implement this method if you need to Serialize (i.e. write) the object
// back to JSON
public override void Write(Utf8JsonWriter writer, UserType value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Here's how you would use the above converter to serialize the JSON string provided in the example, along with how to access the values.
public static Tours ParseJson(string json)
{
Tours tours = JsonSerializer.Deserialize<Tours>(json);
return tours;
}
public static void AccessValues(Tours tours)
{
foreach (UserType data in tours.Types)
{
string typeName = data.Key; // "tour_type"
foreach (KeyValuePair<string, int> pairs in data.Values)
{
string key = pairs.Key; // "groups" or "individual
int value = pairs.Value; // 1 or 2
}
}
}
For what it's worth, Visual Studio suggests the following C# class structure for the example JSON (which is similar to what #Jawad suggested):
public class Rootobject
{
public object[][] types { get; set; }
}
Hope that helps.
I couldn't figure out your JSON so I created an example with verified JSON.
Try this:
JSON:
{
"Items": [
{
"Name": "tour",
"Attributes": [
{
"Name": "groups",
"Value": 1
},
{
"Name": "individual",
"Value": 2
}
]
},
{
"Name": "demo",
"Attributes": [
{
"Name": "this is demo",
"Value": 3
},
{
"Name": "design pattern",
"Value": 99
}
]
}
]
}
Types foo = JsonSerializer.Deserialize<Types>(jsonString);
public class TypeAttribute
{
public string Name { get; set; }
public int Value { get; set; }
}
public class Type
{
private readonly ICollection<TypeAttribute> _attributes;
public Type()
{
_attributes = new Collection<TypeAttribute>();
}
public void AddAttributes(IEnumerable<TypeAttribute> attrs)
{
foreach(TypeAttribute ta in attrs)
{
_attributes.Add(ta);
}
}
public string Name { get; set; }
public IEnumerable<TypeAttribute> Attributes
{
get { return _attributes; }
set
{
foreach(TypeAttribute ta in value)
{
_attributes.Add(ta);
}
}
}
}
public class Types
{
ICollection<Type> _items;
public Types()
{
_items = new Collection<Type>();
}
public void AddItems(IEnumerable<Type> tps)
{
foreach (Type t in tps)
{
_items.Add(t);
}
}
public IEnumerable<Type> Items
{
get { return _items; }
set
{
foreach (Type t in value)
{
_items.Add(t);
}
}
}
}

Serialize and deserialize part of JSON object as string with WCF

I have a WCF REST service which has a resource which contains several typed fields, and then a field which can be an array of objects. I want the field on our service to serialize this field as if it were a string. Example:
[DataContract]
public class User
{
[DataMember]
public long ID;
[DataMember]
public string Logon;
[DataMember]
public string Features;
}
When users of our API POST a new User object, I'd like them to be able to do use something like this as the body:
{
"ID" : 123434,
"Logon" : "MyLogon",
"Features" : [
{ "type": "bigFeature", "size": 234, "display":true },
{ "type": "smFeature", "windowCount": 234, "enableTallness": true}
]
}
instead of
{
"ID" : 123434,
"Logon" : "MyLogon",
"Features" : "[
{ \"type\": \"bigFeature\", \"size\": 234, \"display\":true },
{ \"type\": \"smFeature\", \"windowCount\": 234, \"enableTallness\": true}
]"
}
On the service side, I'm going to be saving the "Features" array as JSON text blog in the database, and when I return the Object on GET calls, I'd like it to round trip properly.
If you were willing to switch to Json.NET, you could serialize your Features string as a private JToken proxy property:
[DataContract]
public class User
{
[DataMember]
public long ID;
[DataMember]
public string Logon;
string _features = null;
[IgnoreDataMember]
public string Features
{
get
{
return _features;
}
set
{
if (value == null)
_features = null;
else
{
JToken.Parse(value); // Throws an exception on invalid JSON.
_features = value;
}
}
}
[DataMember(Name="Features")]
JToken FeaturesJson
{
get
{
if (Features == null)
return null;
return JToken.Parse(Features);
}
set
{
if (value == null)
Features = null;
else
Features = value.ToString(Formatting.Indented); // Or Formatting.None, if you prefer.
}
}
}
Note that, in order to serialize the Features string without escaping, it must be valid JSON, otherwise your outer JSON will be corrupt. I enforce this in the setter. You could use JArray instead of JToken to enforce the requirement that the string represent a JSON array, if you prefer.
Note that the string formatting isn't preserved during serialization.

One class for two JSON responses

I send requests to API and have two responses.
First:
{"response": [{...}, {...}]}
Second:
{"response": {"count": 0, "items": [{...}, {...}]}}
Can I create one class for two cases? I use C# and Json.NET.
Yes, you can use a custom JsonConverter detect the JSON format and deserialize into the same class(es). Below is a converter that can handle both formats. It assumes that you are going to deserialize into a List<Item>.
class ItemListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<Item>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken response = JToken.Load(reader)["response"];
if (response.Type == JTokenType.Array)
{
return response.ToObject<List<Item>>();
}
else
{
return response["items"].ToObject<List<Item>>();
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Here is a demo showing how to use the converter:
class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
string json1 = #"
{
""response"" :
[
{ ""Id"" : 1, ""Name"" : ""Foo"" },
{ ""Id"" : 2, ""Name"" : ""Bar"" },
]
}";
DeserializeAndDump(json1);
string json2 = #"
{
""response"" :
{
""count"" : 2,
""items"" :
[
{ ""Id"" : 3, ""Name"" : ""Fizz"" },
{ ""Id"" : 4, ""Name"" : ""Buzz"" },
]
}
}";
DeserializeAndDump(json2);
}
private static void DeserializeAndDump(string json)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ItemListConverter());
List<Item> list = JsonConvert.DeserializeObject<List<Item>>(json, settings);
foreach (Item item in list)
{
Console.WriteLine("Id: " + item.Id + ", Name: " + item.Name);
}
}
}
Here is the output of the above:
Id: 1, Name: Foo
Id: 2, Name: Bar
Id: 3, Name: Fizz
Id: 4, Name: Buzz
First, if you are not aware of it, there is this online tool http://json2csharp.com/ where you can generate your C# class from your JSON string. I think it could be better to use two different classes. Is there a specific reason to use only one?
You can use JsonExtensionData attribute
Only declare the properties you’re interested in and let extension data do the rest.
public class CustomerInvoice
{
// we're only modifing the tax rate
public decimal TaxRate { get; set; }
// everything else gets stored here
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
}
string json = #"{
'HourlyRate': 150,
'Hours': 40,
'TaxRate': 0.125
}";
var invoice = JsonConvert.DeserializeObject<CustomerInvoice>(json);
// increase tax to 15%
invoice.TaxRate = 0.15m;
string result = JsonConvert.SerializeObject(invoice);
// {
// 'TaxRate': 0.15,
// 'HourlyRate': 150,
// 'Hours': 40
// }
Using extension data to round-trip JSON like this also means you don’t
need to worry about third-party sources adding additional JSON because
it will automatically be preserved when serializing/deserializing.
Nifty.
If you don’t want extension data serialized (or deserialized) then
disable that functionality by setting WriteData and ReadData
properties on ExtensionDataAttribute to false.
Reference: Json.NET 5.0 Release 7 – Immutable Collections
or custom JsonConvert, so look here Deserialize JSON not working with JSON.NET

Categories

Resources