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

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

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

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

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



답변

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

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

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


답변

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

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

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


답변

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

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


답변

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

이 도움을 바랍니다.


답변

클래스 속성을 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 });
            }
        }
    }

도움이되는 희망 …


답변

@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 만 저장합니다 .


답변

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

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

[JsonIgnore]
public string IgnoreOnSerializing;