【问题标题】:Newton CamelCase Issue with underscore带有下划线的 Newton CamelCase 问题
【发布时间】:2020-11-26 17:35:03
【问题描述】:

我注意到,当我序列化具有 HTTP_VERB 键的 C# 字典时,它会在 JSON 结构中变为 httP_VERB 而不是 hTTP_VERB 或 http_verb,我预计骆驼案例会成功。

这是我用来重现问题的代码:

  class Program {
    static void Main(string[] args) {

      var settings = new JsonSerializerSettings();

      settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
      settings.NullValueHandling = NullValueHandling.Ignore;

      var headers = new Dictionary<string, string>();
      headers["SessionID"] = "123456";
      headers["HTTP_VERB"] = "POST";
      headers["HTTPVERSION"] = "1";
      var data = new
      {
        headers = headers
      };

      string serializedEvent = JsonConvert.SerializeObject(data, settings);

      if (serializedEvent.Contains("httP_VERB")) {
        Console.WriteLine("Something is wrong with this camel case");
      }
      else {
        Console.WriteLine("Sucess");
      }

    }
  }

【问题讨论】:

    标签: c# json.net


    【解决方案1】:

    没有办法让CamelCasePropertyNamesContractResolver按照您想要的方式将字符串转换为驼峰式大小写,但您可以轻松编写自己的ContractResolver

    我在 CsCss 项目中使用了 PascalCase 转换。稍作调整后,结果如下:

    public enum IdentifierCase
    {
        Camel,
        Pascal,
    }
    
    public class CustomPropertyNamesContractResolver : DefaultContractResolver
    {
        private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
    
        public CustomPropertyNamesContractResolver (bool shareCache = false)
            : base(shareCache)
        {
            Case = IdentifierCase.Camel;
            PreserveUnderscores = true;
        }
    
        public IdentifierCase Case { get; set; }
        public bool PreserveUnderscores { get; set; }
    
        protected override string ResolvePropertyName (string propertyName)
        {
            return ChangeCase(propertyName);
        }
    
        private string ChangeCase (string s)
        {
            var sb = new StringBuilder(s.Length);
    
            bool isNextUpper = Case == IdentifierCase.Pascal, isPrevLower = false;
            foreach (var c in s) {
                if (c == '_') {
                    if (PreserveUnderscores)
                        sb.Append(c);
                    isNextUpper = true;
                }
                else {
                    sb.Append(isNextUpper ? char.ToUpper(c, Culture) : isPrevLower ? c : char.ToLower(c, Culture));
                    isNextUpper = false;
                    isPrevLower = char.IsLower(c);
                }
            }
            return sb.ToString();
        }
    
        // Json.NET implementation for reference
        private static string ToCamelCase (string s)
        {
            if (string.IsNullOrEmpty(s) || !char.IsUpper(s[0]))
                return s;
            var sb = new StringBuilder();
            for (int i = 0; i < s.Length; ++i) {
                if (i == 0 || i + 1 >= s.Length || char.IsUpper(s[i + 1]))
                    sb.Append(char.ToLower(s[i], Culture));
                else {
                    sb.Append(s.Substring(i));
                    break;
                }
            }
            return sb.ToString();
        }
    }
    

    此转换器支持PascalCasecamelCase。它似乎以您期望的方式转换属性名称。

    我已经从 Json.NET 中留下了原始的 ToCamelCase 函数以供参考。

    示例程序:

    internal class Program
    {
        private static void Main ()
        {
            var obj = new Dictionary<string, string> {
                { "SessionID", "123456" },
                { "HTTP_VERB", "POST" },
                { "HTTPVERSION", "1" },
            };
            var settings = new JsonSerializerSettings {
                Formatting = Formatting.Indented,
                ContractResolver = new CustomPropertyNamesContractResolver()
            };
            string strCamel = JsonConvert.SerializeObject(obj, settings);
            Console.WriteLine("camelCase: \n" + strCamel);
            Console.WriteLine(strCamel.Contains("httP_VERB") ? "Something is wrong with this camel case" : "Success");
            settings.ContractResolver = new CustomPropertyNamesContractResolver {
                Case = IdentifierCase.Pascal,
                PreserveUnderscores = false,
            };
            string strPascal = JsonConvert.SerializeObject(obj, settings);
            Console.WriteLine("PascalCase: \n" + strPascal);
            Console.ReadKey();
        }
    }
    

    输出:

    camelCase:
    {
      "sessionId": "123456",
      "http_Verb": "POST",
      "httpversion": "1"
    }
    Success
    PascalCase:
    {
      "SessionId": "123456",
      "HttpVerb": "POST",
      "Httpversion": "1"
    }
    

    【讨论】:

    • 谢谢,你能解释一下为什么HTTP_VERB会导致httP_VERB吗?为什么只有前 3 个字母变成小写?是什么原因?
    • 这是由詹姆斯选择的奇怪算法引起的(您可以在答案中看到)。如果下一个是大写的,它会降低字符。一旦遇到小写字符,只需复制字符串的其余部分。
    • 我明白了。如果下划线将被视为大写,它将正常工作
    • 谢谢这给了我想法,不幸的是我使用了它,当我反序列化它时它并没有为我工作。我遇到了这个解决方案(蛇盒)gist.github.com/crallen/9238178。它对我有用。
    【解决方案2】:

    我需要以相反的顺序解析,即 JSON 中的小写下划线属性到 .NET 中的 Pascal 非下划线属性。 Discord 的解决方案帮助我做到了这一点:

    public class PascalCasePropertyNamesContractResolver : DefaultContractResolver
    {
        private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
    
        public PascalCasePropertyNamesContractResolver(bool shareCache = false)
            : base(shareCache)
        {
        }
    
        protected override string ResolvePropertyName(string s)
        {
            var sb = new StringBuilder(s.Length);
    
            bool isNextUpper = false, isPrevLower = false;
    
            for (var i = 0; i < s.Length; i++)
            {
                var c = s[i];
    
                sb.Append(char.ToLower(c, Culture));
    
                isNextUpper = i + 1 < s.Length && char.IsUpper(s[i + 1]);
    
                if (isNextUpper && isPrevLower)
                {
                    sb.Append("_");
                }
    
                isPrevLower = char.IsLower(c);
            }
    
            return sb.ToString();
        }
    }
    

    【讨论】:

    • 干得好。但是,轻微的 nitpik:您实际上并不需要创建(又一个)静态引用静态属性 CultureInfo.InvariantCulture。如果您想在循环中阻止(属性)函数调用 CultureInfo.(get_)InvariantCulture,请使用局部变量:var Culture = System.Globalization.CultureInfo.InvariantCulture
    【解决方案3】:

    这对我有帮助。

    var contractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
    contractResolver.NamingStrategy = new Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy();
    settings.ContractResolver = contractResolver;
    dynamic deserializedObject = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(requestResult.Content, settings);
    

    你也可以试试 CamelCaseNamingStrategy() 而不是 SnakeCaseNamingStrategy();

    【讨论】:

      猜你喜欢
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-21
      • 2023-03-10
      • 2011-07-10
      • 2018-10-16
      相关资源
      最近更新 更多