JSON.NET serializer improperly serializes string a

2019-09-14 20:29发布

问题:

I have a class:

[JsonObject(MemberSerialization.OptIn)]
public class UserPreferenceDTO
{
    [JsonProperty]
    public string Name { get; set; }

    [JsonProperty]
    public string Value { get; set; }

    public static explicit operator UserPreferenceDTO(UserPreference pref)
    {
        if (pref == null) return null;
        return new UserPreferenceDTO
        {
            Name = pref.Name,
            Value = pref.Value
        };
    }

    public static explicit operator UserPreference(UserPreferenceDTO pref)
    {
        if (pref == null) return null;
        return new UserPreference
        {
            Name = pref.Name,
            Value = pref.Value
        };
    }
}

and a controller, eg, :

public HttpResponseMessage Post(int caseid, Guid id, UserPreferenceDTO prefs)
{ ... }

NOTE: The controller class is decorated with a [CamelCaseControllerConfig] attribute which does this:

public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
   var formatter = controllerSettings.Formatters.OfType<JsonMediaTypeFormatter>().Single();
   controllerSettings.Formatters.Remove(formatter);

   formatter = new JsonMediaTypeFormatter
   {
       SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
   };

   controllerSettings.Formatters.Add(formatter);

}

On the client I'm sending over an object like this:

{ name: "name", value: "Some value" }

Often value is a JS boolean. The problem is that when it reaches the controller, the boolean is converted to a C# boolean (True/False) and stringified.

For example, sending

'{ "name": "wantColFit", "value": "false" }'

becomes:

in the .NET controller.

If you look at the model (UserPreferenceDTO) definition Value takes a string. So why is the serializer converting the value into a boolean?

I would much rather have the value be preserved as "true"/"false" when it is saved (which would make it easier to parse back to a boolean on the client, since JSON.parse("true") === true but JSON.parse("True") !== true)

回答1:

Json Boolean will be mapped to C# bool. Since your field name is same and on the C# side it is string the bool been converted to string. As per Intermediate Language of .NEt boolean value "true" will become "True" in .Net.

To solve this issue, either you need to pass the boolean value with in quotes or needs to change your C# property.

string valueField = "";

[JsonProperty]
public string Value 
{ 
   get{
       return valueField.ToLower();
    }
   set{
      value = valueField;
   }
}


回答2:

Couldn't figure out how (nor why) Json.Net was deserializing a string like "true" to a C# boolean True so I just borrowed this gist: BooleanConverter.js where ReadJson does:

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var val = reader.Value.ToString().ToLower().Trim();

        if (val == "true" || val == "false")
            return val;

        // If we reach here, we're pretty much going to throw an error so let's let Json.NET throw it's pretty-fied error message.
        return new JsonSerializer().Deserialize(reader, objectType);
    }

and I was able to serialize my model a proper "true" or "false" value (rather than "True" or "False")

If anyone can answer what Json.Net is doing internally to cast the boolean-like strings "true"/"false" I will accept their Answer.