runtime: System.Text.Json deserializing of object[bool] does not produce boolean
I understand that the System.Text.Json is still in development. However, I would like to point out that the new deserializer produces very different results than the previous one.
Scenario:
public class MyRequest
{
public string ParamName { get; set; }
public object ParamValue { get; set; }
}
Controller method:
[HttpPost]
public ActionResult<string> Post([FromBody] MyRequest myRequest)
{
return Content($"Type of '{myRequest.ParamName}' is {myRequest.ParamValue.GetType()}; value is {myRequest.ParamValue}");
}
Posting this json:
{
"ParamName": "Bool param",
"ParamValue": false
}
In .net core 2.1, the false value deserializes as boolean: Type of ‘Bool param’ is System.Boolean; value is False
However, in .net core 3.0 preview 6, the false value deserializes as System.Text.Json.JsonElement: Type of ‘Bool param’ is System.Text.Json.JsonElement; value is False
Will there be any chance to make the new deserializer work the same as in 2.1?
Note: we declare the ParamValue as object, as in the real app the values are of several different types, and so far the deserializer handled all them for us without issues. In 3.0, all this functionality is broken.
Thanks.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 33 (15 by maintainers)
I don’t really like this design. You are making the most common use cases for a parser an impenetrable mess to serve the general correctness. To serve the 2% edge cases, you are making this yet another unusable .NET Json API for the 98% use cases. You have an options bucket, use it to OPT into the generally correct, but unhelpful behavior, not have it be the default.
Yes some global option is doable.
However, it is actually fairly easy to create a custom converter to handle the cases you show above. Earlier I provided the sample for
bool
and here’s a sample forbool
,double
,string
,DateTimeOffset
andDateTime
. I will get the sample added to https://github.com/dotnet/runtime/blob/3e4a06c0e90e65c0ad514d8e2a9f93cb584d775a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.Object.cs#L267Current functionality treats any
object
parameter asJsonElement
when deserializing. The reason is that we don’t know what CLR type to create, and decided as part of the design that the deserializer shouldn’t “guess”.For example, a JSON string could be a DateTime but the deserializer doesn’t attempt to inspect. For a “True” or “False” in JSON that is fairly unambiguous to deserialize to a Boolean, but we don’t since we don’t want to special case String or Number, we don’t want to make an exception for True or False.
With the upcoming preview 7, it is possible to write a custom converter for
object
that changes that behavior. Here’s a sample converter:Used like
For those looking to convert JSON object to Hashtable, feel free to use my example. It should cover all JSON types. But, I have not tested it thoroughly, only for what we are needing it for. I have not used the write either, as we are only reading.
suggest to add a new strategy :
PreferRawObject:true
to use default object mappingundefinedIn most cases when I’ve seen this, it has been an anti-pattern.
object
is rarely what people want, and have seen far too manymodel.Foo.ToString()
from people who didn’t realize they shouldn’t be usingobject
.See the
SystemObjectNewtonsoftCompatibleConverter
sample class in https://github.com/dotnet/runtime/blob/7eea339df0dab9feb1a9b7bf6be66ddcb9924dc9/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.Object.cs#L267 for semantics similar to Json.NET (except for objects and arrays).The JSON->POCO auto-generator in VS creates
object
members when it seesnull
, so this behavior breaks anyone migrating from such a thing.But in most cases, this is again people just letting the default give them a bad model when they should be fixing what it gives them.
FWIW, using an
object
/dynamic
property for message deserialization is a security consideration and should be handled with care:https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-vulnerable-web-API.html
FWIW the new
JsonNode
may help in certain scenarios since it at least allows explicit casts to primitives:If you must have
System.Object
, you can still leverage nodes:As mentioned above, the current design avoids deserializing JSON string and bool to CLR string and bool because this would not be consistent with how JSON numbers are deserialized (since we don’t know the CLR number type and don’t want to “guess”).
Imagine having a CLR
object[]
property and if the JSON has mixed values (strings, bools, numbers), some array elements would be are CLRstring
, somebool
and the numbers would beJsonElement
– that would be more confusing IMHO. Also, Newtonsoft deserializes JSONstring
sometimes as a GUID or DateTime, which can also be confusing and\or unexpected.So if we want to add a Newtonsoft custom converter that is possible, but we can’t change the default behavior in any case since that would break backwards compat.
@steveharter Json and Javascript don’t have many type, but
boolean
is known one.System.Text.Json
should map json known’d type to clr type directly without custom converterundefined