Newtonsoft.Json icon indicating copy to clipboard operation
Newtonsoft.Json copied to clipboard

StringEnumConverter does not contain decodeURI

Open hjkl950217 opened this issue 1 year ago • 2 comments

Env: Js code call http api

Source/destination types

    [Description("启用/禁用")]
    public enum EnableDisable
    {
        [EnumMember(Value = "禁用")]
        Disable = 0,
        [EnumMember(Value = "启用")]
        Enable = 1,
    }

public class DestType { public EnableDisable StatusEnum { get; set; }  } 

Source/destination JSON

{"StatusEnum":"%E7%A6%81%E7%94%A8"}

Expected behavior

Want StringEnumConverter to allow decodeuri before judging enumeration. I think we can use these methods:

  1. Rewrite the StringEnumConverter class and decode it before calling the parent class readjson.This allows users to inherit the code themselves
  2. StringEnumConverter Construction method increase judgment.
  3. Global control, adding decoding configuration

Actual behavior

not support convert string To enum

hjkl950217 avatar Aug 09 '22 09:08 hjkl950217

Fortunately, you can already achieve the desired functionality without requiring changes to the Newtonsoft.Json library.

Unfortunately, it relies on Newtonsoft.Json functionality that is really. really, really poorly and insufficiently documented, in particular how it the respective functionality works in conjunction with StringEnumConverter :sob:

The key to make the functionality you want working is in utilizing StringEnumConverter's NamingStrategy property (which can also be set through one of StringEnumConverter's constructors).

NamingStrategy is used by StringEnumConverter to convert enum values to a particular json representation (think of choosing between camel-case, kebap-case, snake-case, etc...).

Now, importantly, NamingStrategy is a (abstract) class that can be derived from to implement a custom naming strategy, basically exactly what you want (in your case, you want a url-encode naming scheme for your enum values).

In its most basic form, a custom NamingStrategy class that implements the url-encode naming scheme you want could look like this:

public class UrlEncodeNamingStrategy : Newtonsoft.Json.Serialization.NamingStrategy
{
    protected override string ResolvePropertyName(string name)
        => HttpUtility.UrlEncode(name);

    public UrlEncodeNamingStrategy()
    {
        OverrideSpecifiedNames = true;
    }
}

Note that the OverrideSpecifiedNames property of the custom NamingStrategy has to be set to true, otherwise the StringEnumConverter won't utilize the naming strategy.

A full example using the UrlEncodeNamingStrategy from above (dotnetfiddle: https://dotnetfiddle.net/oFvyy0):

var settings = new JsonSerializerSettings
{
    Converters = new JsonConverter[] { new StringEnumConverter(new UrlEncodeNamingStrategy()) }
};

var json1 = @"{""StatusEnum"":""%E7%A6%81%E7%94%A8""}";
var result1 = JsonConvert.DeserializeObject<DestType>(json1, settings);
Console.WriteLine($"result1.StatusEnum = {result1.StatusEnum}");

var json2 = @"{""StatusEnum"":""%e5%90%af%e7%94%a8""}";
var result2 = JsonConvert.DeserializeObject<DestType>(json2, settings);
Console.WriteLine($"result2.StatusEnum = {result2.StatusEnum}");

elgonzo avatar Aug 09 '22 14:08 elgonzo

(I deleted my previous second comment as it turns out that you don't need to do what i suggested there. If the json data for some enum value is just plain text instead of being url-encoded, Newtonsoft.Json deserialization routine still handles this just fine also when a StringEnumConverter with UrlEncodeNamingStrategy is being used. My apologies for talking rubbish earlier :-) )

elgonzo avatar Aug 09 '22 14:08 elgonzo