속성을 deserialize하지만 json.net으로 직렬화하지 않음


123

Json.net으로 C # 객체를 직렬화하여 생성 된 구성 파일이 있습니다.

직렬화 된 클래스의 하나의 속성을 단순한 enum 속성이 아닌 클래스 속성으로 마이그레이션하려고합니다.

이 작업을 수행하는 한 가지 쉬운 방법은 이전 열거 형 속성을 클래스에 남겨두고 Json.net이 구성을로드 할 때이 속성을 읽도록 정렬하지만 다음에 객체를 직렬화 할 때 다시 저장하지 않도록하는 것입니다. 이전 열거 형에서 개별적으로 새 클래스를 생성하는 방법을 다룰 것입니다.

Json.net이 직렬화 할 때만 무시하고 역 직렬화 할 때주의하도록 C # 객체의 속성을 표시 (예 : 특성)하는 간단한 방법이 있습니까?


사용자 정의 변환기는 어떻습니까? 속성의 속성으로 사용하고 ReadJson 및 WriteJson을 다른 부분으로 재정의 할 수 있습니다. 예 (아니 정확하게 당신이 필요하지만 ...) weblogs.asp.net/thangchung/archive/2010/08/26/...
— 라파엘 Althaus

OnDeserialized 속성은 당신을 위해 해결 방법이 될 수
— Estefany 벨 레즈

`[JsonIgnore] '속성을 사용하면 가능하지 않나요 ?? james.newtonking.com/archive/2009/10/23/…
— Juri

q의 마지막 단락에 따라 이것이 한 방향으로 만 사용될 수있는 방법을 확장 할 수 있습니까?
— Will Dean

[JsonProperty] 속성으로 장식 된 보조 private setter와 함께 [JsonIgnore]를 사용할 수 있습니다. 다른 몇 가지 간단한 솔루션도 있습니다. 자세한 글을 추가했습니다.
— Brian Rogers

답변:


133

실제로 원하는 결과를 얻기 위해 사용할 수있는 몇 가지 매우 간단한 접근 방식이 있습니다.

예를 들어 현재 다음과 같이 정의 된 클래스가 있다고 가정 해 보겠습니다.

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

그리고 당신은 이것을 원합니다 :

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

이것을 얻으려면 :

{"ReplacementSetting":{"Value":"Gamma"}}

접근 방식 1 : ShouldSerialize 메서드 추가

Json.NET에는 ShouldSerialize클래스에서 해당 메서드를 검색하여 속성을 조건부로 직렬화하는 기능이 있습니다 .

이 기능을 사용하려면 직렬화하지 않을 속성의 이름으로 대체 되는 부울 ShouldSerializeBlah()메서드를 클래스에 추가합니다 Blah. 이 메서드의 구현은 항상 false.

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

참고 :이 접근 방식이 마음에 들지만 ShouldSerialize메서드 를 도입하여 클래스의 공용 인터페이스를 혼란스럽게하고 싶지 않은 경우 IContractResolver를 사용하여 프로그래밍 방식으로 동일한 작업을 수행 할 수 있습니다 . 문서에서 조건부 속성 직렬화 를 참조하십시오 .

접근 방식 2 : JObject를 사용하여 JSON 조작

JsonConvert.SerializeObject직렬화를 수행하는 데 사용 하는 대신 구성 개체를에로드 JObject한 다음 작성하기 전에 JSON에서 원하지 않는 속성을 제거하기 만하면됩니다. 단지 몇 줄의 추가 코드입니다.

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

접근법 3 : 속성의 영리한 (남용) 사용

  1. 적용 [JsonIgnore]직렬화하지 않으려 속성에 속성을 .
  2. 대체, 개인 추가원래 속성과 동일한 유형을 사용하여 클래스에 속성 setter를 . 해당 속성의 구현을 원래 속성으로 설정합니다.
  3. [JsonProperty]대체 setter에 속성을 적용 하여 원래 속성과 동일한 JSON 이름을 지정합니다.

수정 된 Config클래스 는 다음과 같습니다 .

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

7
우리는 get-properties를 internal로 설정하여 프로젝트 (기본 모델의 내부 통합 특정 수퍼 세트를 사용하며 수퍼 클래스 속성이 직렬화되지 않아야 함)에서이를 해결했습니다. 공개 setter가 있으면 Web Api가 속성을 설정할 수 있었지만 직렬화는 중지되었습니다.
— 다니엘 사이디

7
를 사용하는 것과 함께 JsonPropertyAttributeC # 6.0부터 nameof"매직 문자열"을 사용하는 대신 키워드를 사용할 수 있습니다 . 이렇게하면 리팩토링이 훨씬 쉽고 간단합니다. 또한 이름 변경을 놓친 경우 컴파일러가 경고를 표시합니다. @Brian의 예를 사용하면 다음과 같이 사용됩니다.[JsonProperty(nameof(ObsoleteSetting))]
— Geoff James

1
특히이 레거시 시나리오에서 JsonProperty 선언에서 nameof ()를 사용하는 것은 좋지 않습니다. JSON은 다른 인터페이스와의 외부 (그리고 영원한) 계약을 나타내며 리팩터링하는 경우 JSON 속성의 이름을 변경하고 싶지 않습니다. 이 형식으로 JSON을 생성하는 모든 기존 JSON 파일 및 구성 요소는 호환성을 깨뜨릴 것입니다. 사실, 나중에 속성의 이름을 변경해도 변경되지 않도록 직렬화 된 모든 속성에 전체 이름으로 JsonProperty (…)를 추가하는 것이 좋습니다.
— 탄약 Goettsch

36

deserialization 전용 속성을 내부로 표시하는 것이 허용되는 모든 상황에 대해 특성에 전혀 의존하지 않는 놀랍도록 간단한 솔루션이 있습니다. 속성을 내부 get으로 표시하기 만하면됩니다.

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

이로 인해 기본 설정 / 리졸버 등을 사용하여 올바른 역 직렬화가 이루어 지지만 속성은 직렬화 된 출력에서 ​​제거됩니다.


간단하면서도 영리한 솔루션.
— hbulens 2010

속성은 유효성 검사 모듈에서도 무시됩니다. (따라서 공용 get메서드 에 의존하므로 deserialization을 위해 더 이상 [필수]로 표시 할 수 없습니다 .)
— Martin Hansen

2
이것은 internal및 private. 항상 직렬화됩니다.
— Paul

이것은 나를 위해 작동하지 않았습니다. 역 직렬화 할 때 속성을 찾을 수 없음 오류가 발생했습니다.
— Drew Sumido

31

나는 이것에 대한 속성을 고수하는 것을 좋아합니다. 여기에 속성을 직렬화하지 않고 직렬화하지 않을 필요가 있거나 그 반대의 경우에 사용하는 방법이 있습니다.

1 단계-사용자 지정 속성 생성

public class JsonIgnoreSerializationAttribute : Attribute { }

2 단계-사용자 지정 계약 재조정 생성

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

3 단계-직렬화가 필요하지 않지만 역 직렬화가 필요한 속성 추가

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

4 단계-사용

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

도움이 되었기를 바랍니다! 또한 역 직렬화가 발생할 때 속성이 무시된다는 점에 주목할 가치가 있습니다. 역 직렬화 할 때 기존 방식으로 변환기를 사용합니다.

JsonConvert.DeserializeObject<MyType>(myString);

이 유용한 구현에 감사드립니다. GetSerializableMembers완전히 재정의하는 대신 기본 구현을 확장하는 방법이 있습니까?
— Alain

4
신경 쓰지 마세요. 다음과 같이 간단하다는 것을 깨달았습니다.return base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
— Alain

이것이 왜 최고 등급의 답변이 아닌지 모르겠습니다. 깨끗하고 newtonsoft의 패턴을 따르며하기 쉽습니다. 내가 추가 할 유일한 방법은 사용 전 세계적으로 설정할 수 있다는 것입니다JsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
— 매트 M

1
신경 쓰지 마세요, 이것은 질문자가 요구하는 것을 실제로하지 않습니다. 이것은 기본적으로 JsonIgnore를 다시 만드는 것입니다. 직렬화를 위해 속성을 건너 뛰지 않고 역 직렬화 중에 속성을 설정합니다. GetSerializableMembers 메서드가 읽기인지 쓰기인지 알 수있는 방법이 없으므로 둘 다에 대한 속성을 제외합니다. 이 솔루션은 작동하지 않습니다.
— Matt M

7

setter 속성 사용 :

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

이 도움을 바랍니다.


1
감사. JsonProperty IgnoreOnSerializing에는 속성과 동일한 대문자가 있어야합니다 . nameof(IgnoreOnSerializing)이름을 바꿀 경우 매직 스트링을 피하기 위해 사용 하는 것이 좋습니다 .
— Bendik August Nesbø

5

클래스 속성을 De-Serializable이고 Serializable이 아닌 플래그를 지정하는 방법을 검색하는 데 꽤 오랜 시간을 보낸 후 나는 그런 일이 전혀 없다는 것을 알았습니다. 그래서 두 개의 서로 다른 라이브러리 또는 직렬화 기술 (System.Runtime.Serialization.Json 및 Newtonsoft.Json)을 결합한 솔루션을 생각 해냈고 다음과 같이 작동했습니다.

  • 모든 클래스 및 하위 클래스를 "DataContract"로 플래그 지정하십시오.
  • 클래스 및 하위 클래스의 모든 속성을 "DataMember"로 플래그 지정합니다.
  • 클래스 및 하위 클래스의 모든 속성을 "JsonProperty"로 플래그 지정합니다. 단, 직렬화하지 않기를 원하는 속성은 제외됩니다.
  • 이제 속성을 "JsonIgnore"로 직렬화하지 않으려는 플래그를 지정하십시오.
  • 그런 다음 "Newtonsoft.Json.JsonConvert.SerializeObject"를 사용하여 직렬화하고 "System.Runtime.Serialization.Json.DataContractJsonSerializer"를 사용하여 직렬화 해제합니다.

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

도움이되는 희망 ...


1
브라보 아흐메드 아 불라 즘. 덕분에 많은 일에서 저를 구했습니다. :)
— Sike12

2

@ThoHo의 솔루션과 관련하여 setter를 사용하는 것이 실제로 필요한 모든 것이며 추가 태그가 없습니다.

저에게는 이전에 단일 참조 ID가 있었는데,이를로드하여 새 참조 ID 컬렉션에 추가하려고했습니다. 새 컬렉션에 값을 추가 한 setter 메서드 만 포함하도록 참조 ID의 정의를 변경합니다. Json은 Property에 get 이 없으면 값을 다시 쓸 수 없습니다 . 방법.

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

이 클래스는 이제 이전 버전과 역 호환되며 새 버전에 대한 RefId 만 저장합니다 .


1

Tho Ho의 답변을 기반으로 필드에도 사용할 수 있습니다.

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }

[JsonIgnore]
public string IgnoreOnSerializing;

1

응용 프로그램에서 이것이 발생하는 위치와 속성이 하나 인 경우 수동 으로 수행 할 수있는 한 가지 방법은 속성 값을 null로 설정 한 다음 모델에서 값이 null 인 경우 속성이 무시되도록 지정할 수 있습니다.

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

ASP.NET Core 웹앱에서 작업하는 경우 Startup.cs 파일에서이를 설정하여 모든 모델의 모든 속성에 대해 전역 적으로 설정할 수 있습니다.

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

0

JsonConvert를 사용하면 IgnoreDataMemberAttribute 는 괜찮습니다. 내 표준 라이브러리는 Newton.Json을 참조하지 않으며 [IgnoreDataMember]를 사용하여 개체 직렬화를 제어합니다.

에서 Newton.net의 도움말 문서.


0

Json.net이 직렬화 할 때만 무시하고 역 직렬화 할 때주의하도록 C # 객체의 속성을 표시 (예 : 특성)하는 간단한 방법이 있습니까?

이 글을 쓰는 시점에서 내가 찾은 가장 쉬운 방법은이 로직을 IContractResolver에 포함하는 것 입니다.

후손을 위해 여기에 복사 된 위 링크의 샘플 코드 :

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

모든 대답은 좋지만이 접근 방식은 가장 깨끗한 방법처럼 보였습니다. 실제로는 SkipSerialize 및 SkipDeserialize의 속성에서 속성을 검색하여이를 구현했습니다. 따라서 제어하는 ​​모든 클래스를 마크 업할 수 있습니다. 좋은 질문입니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.