속성을 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
이것은 internalprivate. 항상 직렬화됩니다.
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.