2017-09-28 3 views
5

, 난 당신이 몇 가지 속성에 대한 [JsonProperty(PropertyName = "name")]Json.NET으로 직렬화 할 때 모든 속성 이름을 다시 매핑하는 일반적인 규칙을 적용하는 방법은 무엇입니까? 닷넷 <code>type</code>로 JSON 객체를 직렬화 복원 할 때 필드 이름이 일치하지 않는 경우

이 괜찮 멋쟁이로 type의 속성을 장식 할 수 있습니다 발견 돈 일치하지 않지만 컨벤션이나 규칙을 설정하는 방법이 있습니까?

JSON

{ 
    "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 (예 : Job # -> JobNumber)으로 바꾸시겠습니까?

사용자 정의 ContractResolver이 유일한 해결책 인 것처럼 보이지만 공백을 제거하고 "번호"로 "#"을 대체하는 방법을 알아낼 수 없습니다. 누구든지 참조 예가 있습니까?

아니면 간과 한 멋진 간단한 해결책이 있기를 바랍니다.

P. 또한 더 나은 제목에 대한 제안을 수락합니다.

+1

제목은? * Json.NET과 직렬화 할 때 모든 속성 이름을 다시 매핑하는 일반적인 규칙을 적용하는 방법 – dbc

답변

2

는 Json.NET 9.0.1 작업하는 이상, 이것은 사용자 정의 NamingStrategy 수행 할 수 있습니다.예를 들어, 여기에서 제임스 뉴튼 - 왕 SnakeCaseNamingStrategyStringUtils.ToSnakeCase()을 기반으로 :

[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이 될 것입니다 생성 다음과 같이

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(); 
    } 
} 

그런 다음 당신이 당신의 유형에 적용 할 수 있습니다 :

{ 
    "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 속성 이름으로 매핑하며 반대의 경우도 마찬가지입니다. 따라서 "pluck them out"대신에 공백을 삽입하고 "Number"를 "#"로 바꿔야합니다. 그런 다음 매핑이 계약 리졸버에 의해 캐싱되고 역 직렬화 중에 역방향 조회가 수행됩니다.

1

예 a ContractResolver은가는 길입니다.

문제는 대상 속성에서 소스 (예 : "JobType" -> "Job Type")로만 작동하는 것 같습니다. 다른 방식으로는 사용할 수 없습니다. 이렇게하면 솔루션이 원하는 것보다 약간 벗겨 질 수 있습니다.

먼저 우리가 DefaultContractResolver 상속, 우리의 ContractResolver 수 있도록, 그래서 떨어져 비트에서 정상적으로 모든 작품은 우리가 사용자 정의 할 : 우리 직렬화에서 다음

public class JobContractResolver : DefaultContractResolver 
{ 
    protected override string ResolvePropertyName(string propertyName) 
    { 
     // first replace all capital letters with space then letter ("A" => " A"). This might include the first letter, so trim the result. 
     string result = Regex.Replace(propertyName, "[A-Z]", x => " " + x.Value).Trim(); 

     // now replace Number with a hash 
     result = result.Replace("Number", "#"); 

     return result; 
    } 
} 

을, 우리는 JsonSerializerSettingsContractResolver을 설정

당신을 가정
static void Main(string[] args) 
{ 
    string input = @"{""Job #"": ""1"", ""Job Type"": ""A""}"; 

    var job1 = JsonConvert.DeserializeObject<Job1>(input, new JsonSerializerSettings 
    { 
     ContractResolver = new JobContractResolver() 
    }); 

    Console.WriteLine("JobType: {0}", job1.JobType); 
    Console.WriteLine("JobNumber: {0}", job1.JobNumber); 
}