当前位置: 首页 > 面试题库 >

使用Json.NET进行序列化时,如何应用通用规则重新映射所有属性名称?

盖博简
2023-03-14
问题内容

将Json对象反序列化为.Net时type,如果字段名称不匹配,我发现您可以使用修饰您type的属性[JsonProperty(PropertyName = "name")]

对于几个不匹配的属性,这很好而且很花哨,但是有没有办法设置约定或规则?

杰森

{
  "Job": [
    {
      "Job #": "1",
      "Job Type": "A",
    }
  ]
}

C#

    [JsonProperty(PropertyName = "Job Type")]
    public string JobType { get; set; }

    [JsonProperty(PropertyName = "Job #")]
    public string JobNumber { get; set; }

我想知道有很多使用相似名称的字段,有没有办法告诉您设置规则以始终删除空格(EG:)Job Type -> JobType并替换#Number(eg:)Job # -> JobNumber

看起来自定义ContractResolver可能是唯一的解决方案,但是我似乎无法弄清楚如何使用它来抽取空格并将“#”替换为“
Number”。有人参考示例吗?

或者,我希望有一个很好的简单解决方案被我忽略了。

PS也接受更好的标题建议。


问题答案:

假设您正在使用Json.NET
9.0.1或更高版本,则可以使用custom来完成NamingStrategy。举例来说,这里是基于一个SnakeCaseNamingStrategyStringUtils.ToSnakeCase()詹姆斯·牛顿王:

public class CustomNamingStrategy : NamingStrategy
{
    public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames)
    {
        ProcessDictionaryKeys = processDictionaryKeys;
        OverrideSpecifiedNames = overrideSpecifiedNames;
    }

    public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames, bool processExtensionDataNames)
        : this(processDictionaryKeys, overrideSpecifiedNames)
    {
        ProcessExtensionDataNames = processExtensionDataNames;
    }

    public CustomNamingStrategy()
    {
    }

    protected override string ResolvePropertyName(string name)
    {
        return SpaceWords(name);
    }

    enum WordState
    {
        Start,
        Lower,
        Upper,
        NewWord
    }

    static string SpaceWords(string s)
    {
        // Adapted from StringUtils.ToSnakeCase()
        // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/StringUtils.cs#L191
        // 
        // Copyright (c) 2007 James Newton-King
        //
        // Permission is hereby granted, free of charge, to any person
        // obtaining a copy of this software and associated documentation
        // files (the "Software"), to deal in the Software without
        // restriction, including without limitation the rights to use,
        // copy, modify, merge, publish, distribute, sublicense, and/or sell
        // copies of the Software, and to permit persons to whom the
        // Software is furnished to do so, subject to the following
        // conditions:
        //
        // The above copyright notice and this permission notice shall be
        // included in all copies or substantial portions of the Software.
        //
        // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
        // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
        // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
        // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
        // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
        // OTHER DEALINGS IN THE SOFTWARE.

        char wordBreakChar = ' ';

        if (string.IsNullOrEmpty(s))
        {
            return s;
        }

        StringBuilder sb = new StringBuilder();
        WordState state = WordState.Start;

        for (int i = 0; i < s.Length; i++)
        {
            if (s[i] == ' ')
            {
                if (state != WordState.Start)
                {
                    state = WordState.NewWord;
                }
            }
            else if (char.IsUpper(s[i]))
            {
                switch (state)
                {
                    case WordState.Upper:
                        bool hasNext = (i + 1 < s.Length);
                        if (i > 0 && hasNext)
                        {
                            char nextChar = s[i + 1];
                            if (!char.IsUpper(nextChar) && nextChar != ' ')
                            {
                                sb.Append(wordBreakChar);
                            }
                        }
                        break;
                    case WordState.Lower:
                    case WordState.NewWord:
                        sb.Append(wordBreakChar);
                        break;
                }

                sb.Append(s[i]);

                state = WordState.Upper;
            }
            else if (s[i] == wordBreakChar)
            {
                sb.Append(wordBreakChar);
                state = WordState.Start;
            }
            else
            {
                if (state == WordState.NewWord)
                {
                    sb.Append(wordBreakChar);
                }

                sb.Append(s[i]);
                state = WordState.Lower;
            }
        }

        sb.Replace("Number", "#");
        return sb.ToString();
    }
}

然后可以将其应用于您的类型,如下所示:

[JsonObject(NamingStrategyType = typeof(CustomNamingStrategy))]
public class RootObject
{
    public string JobType { get; set; }

    public string JobNumber { get; set; }

    public int JobItemCount { get; set; }

    public string ISOCode { get; set; }

    public string SourceXML { get; set; }
}

并且生成的JSON将如下所示:

{
  "Job Type": "job type",
  "Job #": "01010101",
  "Job Item Count": 3,
  "ISO Code": "ISO 9000",
  "Source XML": "c:\temp.xml"
}

笔记:

  • 如果要将策略应用于已通过指定属性名称的属性JsonPropertyAttribute.PropertyName,请设置NamingStrategy.OverrideSpecifiedNames == true

  • 要将命名策略应用于所有类型而不是在每个对象上进行设置,可以在中设置命名策略DefaultContractResolver.NamingStrategy,然后在中设置合同解析器JsonSerializerSettings.ContractResolver

  • 命名策略从c#属性名称映射到JSON属性名称,反之亦然。因此,您需要插入空格,而不是“拔出”并将“ Number”替换为“#”。然后由合同解析器缓存该映射,并在反序列化过程中进行反向查找。



 类似资料:
  • 问题内容: 我试图弄清楚如何从我的API返回一个核心对象 其中T是一些具有属性的对象,例如 使用 将属性返回为,正确的是。 但是,如果我想使用名称作为类型呢?因此,响应Json实际上将包含一个名为not 的属性,如下所示。 我很好奇是否有一个相对简单的方法可以用Json.net做到这一点,或者您是否必须创建一个自定义并在编写时使用反射来获取类型名称? 谢谢。 问题答案: 据我所知,没有内置的方法可

  • 问题内容: 我正在从如下所示的Web API接收JSON数据: 我将此数据反序列化为以下类型的对象: 稍后在我的应用程序中,我想再次将ErrorDetails对象序列化为JSON,但使用属性名称代替。因此结果将如下所示: 有什么简单的方法可以使用Json.Net完成此操作?也许使用自定义解析器和一些属性,例如: 但是解析器不会告诉我何时进行序列化或反序列化。 问题答案: 您可以使用,Contrac

  • 问题内容: 我想将二维数组反序列化为.net对象的集合。原因是,数组语法将使我的用户更容易在输入文件中使用。所以我只想将数组的索引映射到目标类型的特定属性。 EG具有: 我会得到一个Person的两个实例: 其中内部数组的索引0映射到,索引1映射到,索引2映射到; 有没有一种扩展Json.NET的方法,以便我可以在反序列化期间进行映射,从而隐藏实现细节?我一直在玩一个自定义,但是我没有找到太多关于

  • 问题内容: 我正在使用JSON.NET对对象进行序列化以连接到REST API。我对象中需要序列化为JSON的属性之一具有动态属性名称。如果此属性的struct中包含的值是数字值,则JSON属性为“ type_id”,但是,如果此值是字符串值,则JSON属性名称为“ type_code”。我尝试为此使用自定义,但是在尝试序列化时收到以下消息: “状态为Property的令牌PropertyName

  • 问题内容: 在Json.NET中,如何进行反序列化时需要的所有属性?我知道我可以使用消息中的属性来执行此操作,但是我不想这样做。主要是因为这将要求我的消息库具有外部依赖关系。 我尝试了MissingMemberHandling.Error设置,但它与我想要的相反。我对具有额外属性的JSON表示满意。当JSON中缺少任何目标对象属性时,我希望它失败。 我实际上是反序列化为F#记录,并且这些属性通常不

  • 问题内容: 我在pandas中有一个时序如下: 我想将其重新采样为具有15分钟时间步长的常规时间序列,其中值是线性插值的。基本上我想得到: 但是使用熊猫的重采样方法(df.resample(‘15Min’))我得到: 我尝试使用不同的“ how”和“ fill_method”参数进行重采样方法,但从未获得我想要的结果。我使用了错误的方法吗? 我认为这是一个相当简单的查询,但我在网上搜索了一段时间,