Your Note class needs a parameterless constructor
class Note
{
public DateTime currentDate { get; set; }
public string summary { get; set; }
// add this
public Note()
{
}
public Note(DateTime _date, string _sum)
{
currentDate = _date;
summary = _sum;
}
}
It might be worth thinking if you need your original two parameter constructor. If you removed it, then you could instantiate a new Note like this
var completeNote = new Note
{
currentdate = date,
summary = givenNote
};
Answer from Kevin Brydon on Stack OverflowSystem.Text.Json failure to deserialize strongly typed models from string
System.Text.Json fails to deserialize into a model with an ImmutableList property.
c# - System.Text.Json.deserialize Json String - Stack Overflow
c# - Deserialize json in a "TryParse" way - Stack Overflow
Videos
Don't know why you want to use the JsonNode.Parse().
If you just want to modify the location's value, maybe you can use the JsonSerializer with BookStore and Book class to help you.
The Book class is a IEnumerable property of BookStore.
an example by .NET6 Console App
BookClass is a 2-level nested class
// See https://aka.ms/new-console-template for more information
using System.Text.Json;
// Initialize json string
string jsonStr = "{\"id\": 1, \"ShopName\" :\"Paint Shop\", \"Books\" : [\r\n {\r\n \"bookId\":1,\r\n \"bookName\":\"Peter Pan\",\r\n \"location\":\"A01\"\r\n },\r\n {\r\n \"bookId\":2,\r\n \"bookName\":\"Cooking Book\",\r\n \"location\":\"A02\"\r\n }\r\n ]\r\n }";
// Deserilize to object
Bookstore bookstore = JsonSerializer.Deserialize<Bookstore>(jsonStr);
// Write the location's value
Console.WriteLine(bookstore.Books.ToArray()[1].location);
// Modified the location's value
bookstore.Books.ToArray()[1].location += "_Modified";
// Write the modified location's value
Console.WriteLine(bookstore.Books.ToArray()[1].location);
// See result output
Console.ReadLine();
public class Bookstore
{
public int id { get; set; }
public string ShopName { get; set; }
public IEnumerable<Book> Books { get; set; }
}
public class Book
{
public int bookId { get; set; }
public string bookName { get; set; }
public string location { get; set; }
}
Result output image

If you persist to use JsonNode.Parse(), then need more steps. like this.
// See https://aka.ms/new-console-template for more information
using System.Text.Json;
using System.Text.Json.Nodes;
// Initialize json string
string jsonStr = "{\"id\": 1, \"ShopName\" :\"Paint Shop\", \"Books\" : [\r\n {\r\n \"bookId\":1,\r\n \"bookName\":\"Peter Pan\",\r\n \"location\":\"A01\"\r\n },\r\n {\r\n \"bookId\":2,\r\n \"bookName\":\"Cooking Book\",\r\n \"location\":\"A02\"\r\n }\r\n ]\r\n }";
// Parse to jsonNode
JsonNode jsonNode = JsonNode.Parse(jsonStr);
// Convert to JsonObject
JsonObject jsonObject = jsonNode.AsObject();
// Convert Books to Json Array
JsonArray jsonArray = jsonObject["Books"].AsArray();
// Convert index 1 in array to Json object
JsonObject book1Object = jsonArray[1].AsObject();
// Write the location value
Console.WriteLine(book1Object["location"]);
// Modify location value
book1Object["location"] += "_modified";
// Write the modified location value
Console.WriteLine(book1Object["location"]);
// See output
Console.ReadLine();
public class Bookstore
{
public int id { get; set; }
public string ShopName { get; set; }
public IEnumerable<Book> Books { get; set; }
}
public class Book
{
public int bookId { get; set; }
public string bookName { get; set; }
public string location { get; set; }
}
Other suggestion
Use Newtonsoft.Json is better. Because when convert a json to a C# object, will need to convert many properties with different value type.
The Newtonsoft.Json had already handle these situation, at the mean time the System.Text.Json does not.
You can use JObject to modify json directly
var jObject = JObject.Parse(json);
jObject["Books"][1]["location"] = "new value";
Result:

Note:
You should add Newtonsoft.Json package.
@Victor LG's answer using Newtonsoft is close, but it doesn't technically avoid the a catch as the original poster requested. It just moves it elsewhere. Also, though it creates a settings instance to enable catching missing members, those settings aren't passed to the DeserializeObject call so they are actually ignored.
Here's a "catch free" version of his extension method that also includes the missing members flag. The key to avoiding the catch is setting the Error property of the settings object to a lambda which then sets a flag to indicate failure and clears the error so it doesn't cause an exception.
public static bool TryParseJson<T>(this string @this, out T result)
{
bool success = true;
var settings = new JsonSerializerSettings
{
Error = (sender, args) => { success = false; args.ErrorContext.Handled = true; },
MissingMemberHandling = MissingMemberHandling.Error
};
result = JsonConvert.DeserializeObject<T>(@this, settings);
return success;
}
Here's an example to use it:
if(value.TryParseJson(out MyType result))
{
// Do something with result…
}
With Json.NET you can validate your json against a schema:
string schemaJson = @"{
'status': {'type': 'string'},
'error': {'type': 'string'},
'code': {'type': 'string'}
}";
JsonSchema schema = JsonSchema.Parse(schemaJson);
JObject jobj = JObject.Parse(yourJsonHere);
if (jobj.IsValid(schema))
{
// Do stuff
}
And then use that inside a TryParse method.
public static T TryParseJson<T>(this string json, string schema) where T : new()
{
JsonSchema parsedSchema = JsonSchema.Parse(schema);
JObject jObject = JObject.Parse(json);
return jObject.IsValid(parsedSchema) ?
JsonConvert.DeserializeObject<T>(json) : default(T);
}
Then do:
var myType = myJsonString.TryParseJson<AwsomeType>(schema);
Update:
Please note that schema validation is no longer part of the main Newtonsoft.Json package, you'll need to add the Newtonsoft.Json.Schema package.
Update 2:
As noted in the comments, "JSONSchema" have a pricing model, meaning it isn't free. You can find all the information here
I've had a look at your fiddle and spotted a couple of problems. Working fiddle here
System.Text.Jsonis case-sensitive by default (except for web apps). You can resolve this by using eitherPropertyNamingPolicy = JsonNamingPolicy.CamelCaseorPropertyNameCaseInsensitive = truein the serializer options.The second issue is outlined in Enums as strings
By default, enums are serialized as numbers. To serialize enum names as strings, use the JsonStringEnumConverter.
You should add
JsonSerializerOptionsto resolve (1) and (2):var options = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; var items = JsonSerializer.Deserialize<List<MenuItem>>(json, options);The third issue appears to be with the binding in the constructor for the list of
Permissions. In the constructor you define aList<Permission>for the permissions parameter. I receive the error in your question unless the constructor argument type matches the model property type exactly. So, I updated the constructor to take aIReadOnlyList<Permission>and it deserializes successfully:[JsonConstructor] public MenuItem ( string name, string url, IReadOnlyList<Permission> permissions ) { this.Name = name; this.Url = url; this.Permissions = permissions ?? new List<Permission>().AsReadOnly(); }Alternatively, you could change the
Permissionsproperty toList<Permission>.
This answer to a question with a similar problem explains that this is actually a limitation of System.Text.Json and there is currently an open github issue.
A working fork of your fiddle is demoed here.
I had the same issue. The solution is to use
JsonSerializable
attribute of
System.Text.Json.Serialization
It looks like:
[JsonSerializable(typeof(YourClassName))]
public class YourClassName
{
public byte YourPropertyName { get; set; }
}
Always class and always public and always properties and always use the attribute on the class.
The new
System.Text.Jsonapi exposes aJsonConverterapi which allows us to convert the type as we like.For example, we can create a generic
numbertostringconverter:public class AutoNumberToStringConverter : JsonConverter<object> { public override bool CanConvert(Type typeToConvert) { return typeof(string) == typeToConvert; } public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if(reader.TokenType == JsonTokenType.Number) { return reader.TryGetInt64(out long l) ? l.ToString(): reader.GetDouble().ToString(); } if(reader.TokenType == JsonTokenType.String) { return reader.GetString(); } using(JsonDocument document = JsonDocument.ParseValue(ref reader)){ return document.RootElement.Clone().ToString(); } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { writer.WriteStringValue( value.ToString()); } }When working with MVC/Razor Page, we can register this converter in startup:
services.AddControllersWithViews().AddJsonOptions(opts => { opts.JsonSerializerOptions.PropertyNameCaseInsensitive= true; opts.JsonSerializerOptions.Converters.Insert(0, new AutoNumberToStringConverter()); });and then the MVC/Razor will handle the type conversion automatically.
Or if you like to control the serialization/deserialization manually:
var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }; opts.Converters.Add(new AutoNumberToStringConverter()); var o = JsonSerializer.Deserialize<Product>(json,opts) ;In a similar way you can enable string to number type conversion as below :
public class AutoStringToNumberConverter : JsonConverter<object> { public override bool CanConvert(Type typeToConvert) { // see https://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number switch (Type.GetTypeCode(typeToConvert)) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Single: return true; default: return false; } } public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if(reader.TokenType == JsonTokenType.String) { var s = reader.GetString() ; return int.TryParse(s,out var i) ? i : (double.TryParse(s, out var d) ? d : throw new Exception($"unable to parse {s} to number") ); } if(reader.TokenType == JsonTokenType.Number) { return reader.TryGetInt64(out long l) ? l: reader.GetDouble(); } using(JsonDocument document = JsonDocument.ParseValue(ref reader)){ throw new Exception($"unable to parse {document.RootElement.ToString()} to number"); } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { var str = value.ToString(); // I don't want to write int/decimal/double/... for each case, so I just convert it to string . You might want to replace it with strong type version. if(int.TryParse(str, out var i)){ writer.WriteNumberValue(i); } else if(double.TryParse(str, out var d)){ writer.WriteNumberValue(d); } else{ throw new Exception($"unable to parse {str} to number"); } } }
If you only want to deserialize strings as numbers, you could simply set the NumberHandling property to AllowReadingFromString in the options:
var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
{
// [...]
NumberHandling = JsonNumberHandling.AllowReadingFromString
});
Note: This will automatically deserialize strings as numbers, but not numbers as string. If you need more advanced logic, you will need a custom converter (see other answers).
So I have a json string like:
"{\"thisisnorthecorrectname\":\"Value1\"}"Now I want to use System.Text.Json to check if this string can be deserialized into this class:
internal class Test {
public string Name { get; set; }
}I have tried with a method like this:
static bool CanDeserialize<T>(string jsonString, out T deserializedObject) {
deserializedObject = default;
try {
deserializedObject = JsonSerializer.Deserialize<T>(jsonString);
return true;
}
catch (JsonException) {
return false;
}
}But this does not throw an Exception. Instead it gives me a class object where the property name is NULL.
How can I check if the json string can be deserialized properly into a class?
Is polymorphic deserialization possible in System.Text.Json?
The answer is yes and no, depending on what you mean by "possible".
There is no polymorphic deserialization (equivalent to Newtonsoft.Json's TypeNameHandling) support built-in to System.Text.Json prior to .NET 7. This is because reading the .NET type name specified as a string within the JSON payload (such as $type metadata property) to create your objects is not recommended since it introduces potential security concerns (see https://github.com/dotnet/corefx/issues/41347#issuecomment-535779492 for more info).
Allowing the payload to specify its own type information is a common source of vulnerabilities in web applications.
However, there is a way to add your own support for polymorphic deserialization by creating a JsonConverter<T>, so in that sense, it is possible.
The docs show an example of how to do that using a type discriminator property: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization
Let's look at an example.
Say you have a base class and a couple of derived classes:
public class BaseClass
{
public int Int { get; set; }
}
public class DerivedA : BaseClass
{
public string Str { get; set; }
}
public class DerivedB : BaseClass
{
public bool Bool { get; set; }
}
You can create the following JsonConverter<BaseClass> that writes the type discriminator while serializing and reads it to figure out which type to deserialize. You can register that converter on the JsonSerializerOptions.
public class BaseClassConverter : JsonConverter<BaseClass>
{
private enum TypeDiscriminator
{
BaseClass = 0,
DerivedA = 1,
DerivedB = 2
}
public override bool CanConvert(Type type)
{
return typeof(BaseClass).IsAssignableFrom(type);
}
public override BaseClass Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
if (!reader.Read()
|| reader.TokenType != JsonTokenType.PropertyName
|| reader.GetString() != "TypeDiscriminator")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.Number)
{
throw new JsonException();
}
BaseClass baseClass;
TypeDiscriminator typeDiscriminator = (TypeDiscriminator)reader.GetInt32();
switch (typeDiscriminator)
{
case TypeDiscriminator.DerivedA:
if (!reader.Read() || reader.GetString() != "TypeValue")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
baseClass = (DerivedA)JsonSerializer.Deserialize(ref reader, typeof(DerivedA));
break;
case TypeDiscriminator.DerivedB:
if (!reader.Read() || reader.GetString() != "TypeValue")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
baseClass = (DerivedB)JsonSerializer.Deserialize(ref reader, typeof(DerivedB));
break;
default:
throw new NotSupportedException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject)
{
throw new JsonException();
}
return baseClass;
}
public override void Write(
Utf8JsonWriter writer,
BaseClass value,
JsonSerializerOptions options)
{
writer.WriteStartObject();
if (value is DerivedA derivedA)
{
writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedA);
writer.WritePropertyName("TypeValue");
JsonSerializer.Serialize(writer, derivedA);
}
else if (value is DerivedB derivedB)
{
writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedB);
writer.WritePropertyName("TypeValue");
JsonSerializer.Serialize(writer, derivedB);
}
else
{
throw new NotSupportedException();
}
writer.WriteEndObject();
}
}
This is what serialization and deserialization would look like (including comparison with Newtonsoft.Json):
private static void PolymorphicSupportComparison()
{
var objects = new List<BaseClass> { new DerivedA(), new DerivedB() };
// Using: System.Text.Json
var options = new JsonSerializerOptions
{
Converters = { new BaseClassConverter() },
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(objects, options);
Console.WriteLine(jsonString);
/*
[
{
"TypeDiscriminator": 1,
"TypeValue": {
"Str": null,
"Int": 0
}
},
{
"TypeDiscriminator": 2,
"TypeValue": {
"Bool": false,
"Int": 0
}
}
]
*/
var roundTrip = JsonSerializer.Deserialize<List<BaseClass>>(jsonString, options);
// Using: Newtonsoft.Json
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(objects, settings);
Console.WriteLine(jsonString);
/*
[
{
"$type": "PolymorphicSerialization.DerivedA, PolymorphicSerialization",
"Str": null,
"Int": 0
},
{
"$type": "PolymorphicSerialization.DerivedB, PolymorphicSerialization",
"Bool": false,
"Int": 0
}
]
*/
var originalList = JsonConvert.DeserializeObject<List<BaseClass>>(jsonString, settings);
Debug.Assert(originalList[0].GetType() == roundTrip[0].GetType());
}
Here's another StackOverflow question that shows how to support polymorphic deserialization with interfaces (rather than abstract classes), but a similar solution would apply for any polymorphism: Is there a simple way to manually serialize/deserialize child objects in a custom converter in System.Text.Json?
Polymorphic serialization of whitelisted inherited types has been implemented in .NET 7, and is available in Preview 6.
From the documentation page What’s new in System.Text.Json in .NET 7: Type Hierarchies:
System.Text.Json now supports polymorphic serialization and deserialization of user-defined type hierarchies. This can be enabled by decorating the base class of a type hierarchy with the new
JsonDerivedTypeAttribute.
First, let's consider serialization. Say you have the following type hierarchy:
public abstract class BaseType { } // Properties omitted
public class DerivedType1 : BaseType { public string Derived1 { get; set; } }
public class DerivedType2 : BaseType { public int Derived2 { get; set; } }
And you have a data model that includes a value whose declared type is BaseType, e.g.
var list = new List<BaseType> { new DerivedType1 { Derived1 = "value 1" } };
In previous versions, System.Text.Json would only serialize the properties of the declared type BaseType. Now you will be able to include the properties of DerivedType1 when serializing a value declared as BaseType by adding [JsonDerivedType(typeof(TDerivedType))] to BaseType for all derived types:
[JsonDerivedType(typeof(DerivedType1))]
[JsonDerivedType(typeof(DerivedType2))]
public abstract class BaseType { } // Properties omitted
Having whitelisted DerivedType1 in this manner, serialization of your model:
var json = JsonSerializer.Serialize(list);
Results in
[{"Derived1" : "value 1"}]
Demo fiddle #1 here.
Do note that only derived types whitelisted via attribute (or through setting JsonTypeInfo.PolymorphismOptions in runtime) can be serialized via this mechanism. If you have some other derived type which is not whitelisted, e.g.:
public class DerivedType3 : BaseType { public string Derived3 { get; set; } }
Then JsonSerializer.Serialize(new BaseType [] { new DerivedType3 { Derived3 = "value 3" } }) will throw a System.NotSupportedException: Runtime type 'DerivedType3' is not supported by polymorphic type 'BaseType' exception. Demo fiddle #2 here.
That covers serialization. If you need to round-trip your type hierarchy, you will need to supply a type discriminator property value to use for each derived type. This may be done providing a value for JsonDerivedTypeAttribute.TypeDiscriminator for each derived type:
[JsonDerivedType(typeof(DerivedType1), "DerivedType1")]
[JsonDerivedType(typeof(DerivedType2), "DerivedType2")]
public abstract class BaseType { } // Properties omitted
Now when you serialize your model
var json = JsonSerializer.Serialize(list);
System.Text.Json will add an artificial type discriminator property "$type" indicating the type that was serialized:
[{"$type" : "DerivedType1", "Derived1" : "value 1"}]
Having done so, you can now deserialize your data model like so:
var list2 = JsonSerializer.Deserialize<List<BaseType>>(json);
And the actual, concrete type(s) serialized will be preserved. Demo fiddle #3 here.
It is also possible to inform System.Text.Json of your type hierarchy in runtime via Contract Customization. You might need to do this when your type hierarchy cannot be modified, or when some derived types are in different assemblies and cannot be referenced at compile time, or you are trying to interoperate between multiple legacy serializers. The basic workflow here will be to instantiate an instance of DefaultJsonTypeInfoResolver and add a modifier which sets up the necessary PolymorphismOptions for the JsonTypeInfo for your base type.
For example, polymorphic serialization for the BaseType hierarchy can be enabled in runtime like so:
var resolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
// Add an Action<JsonTypeInfo> modifier that sets up the polymorphism options for BaseType
static typeInfo =>
{
if (typeInfo.Type != typeof(BaseType))
return;
typeInfo.PolymorphismOptions = new()
{
DerivedTypes =
{
new JsonDerivedType(typeof(DerivedType1), "Derived1"),
new JsonDerivedType(typeof(DerivedType2), "Derived2")
}
};
},
// Add other modifiers as required.
}
};
var options = new JsonSerializerOptions
{
TypeInfoResolver = resolver,
// Add other options as required
};
var json = JsonSerializer.Serialize(list, options);
Demo fiddle #4 here.
Notes:
The whitelisting approach is consistent with the approach of the data contract serializers, which use the
KnownTypeAttribute, andXmlSerializer, which usesXmlIncludeAttribute. It is inconsistent with Json.NET, whoseTypeNameHandlingserializes type information for all types unless explicitly filtered via a serialization binder.Allowing only whitelisted types to be deserialized prevents Friday the 13th: JSON Attacks type injection attacks including those detailed in TypeNameHandling caution in Newtonsoft Json and External json vulnerable because of Json.Net TypeNameHandling auto?.
Integers as well as strings may be used for the type discriminator name. If you define your type hierarchy as follows:
[JsonDerivedType(typeof(DerivedType1), 1)] [JsonDerivedType(typeof(DerivedType2), 2)] public abstract class BaseType { } // Properties omittedThen serializing the list above results in
[{"$type" : 1, "Derived1" : "value 1"}]Numeric type discriminator values are not used by Newtonsoft however, so if you are interoperating with a legacy serializer you might want to avoid this.
The default type discriminator property name,
"$type", is the same type discriminator name used by Json.NET. If you would prefer to use a different property name, such as the name"__type"used byDataContractJsonSerializer, applyJsonPolymorphicAttributeto the base type and setTypeDiscriminatorPropertyNamelike so:[JsonPolymorphic(TypeDiscriminatorPropertyName = "__type")] [JsonDerivedType(typeof(DerivedType1), "DerivedType1")] [JsonDerivedType(typeof(DerivedType2), "DerivedType2")] public abstract class BaseType { } // Properties omittedIf you are interoperating with Json.NET (or DataContractJsonSerializer), you may set the value of
TypeDiscriminatorequal to the type discriminator value used by the legacy serializer.If the serializer encounters a derived type that has not been whitelisted, you can control its behavior by setting
JsonPolymorphicAttribute.UnknownDerivedTypeHandlingto one of the following values:JsonUnknownDerivedTypeHandling Value Meaning FailSerialization 0 An object of undeclared runtime type will fail polymorphic serialization. FallBackToBaseType 1 An object of undeclared runtime type will fall back to the serialization contract of the base type. FallBackToNearestAncestor 2 An object of undeclared runtime type will revert to the serialization contract of the nearest declared ancestor type. Certain interface hierarchies are not supported due to diamond ambiguity constraints.