web-dev-qa-db-ja.com

プロパティをシリアル化しますが、Json.Netではプロパティを逆シリアル化しません

特定のプロパティをシリアル化しないように逆シリアル化する方法はたくさんありますが、逆の動作を探しています。

私は逆を尋ねるたくさんの質問を見つけました:

プロパティを逆シリアル化するが、json.netではシリアル化しない

Json.NETに特定のプロパティを逆シリアル化するように指示できますが、シリアル化はできませんか?

JSON.Net-シリアル化時にのみJsonIgnoreAttributeを使用します(ただし、逆シリアル化時には使用しないでください)

特定のプロパティをシリアル化するが、POCOに逆シリアル化されないようにするにはどうすればよいですか?特定のプロパティを装飾するために使用できる属性はありますか?

基本的に、逆シリアル化のためのShouldSerialize *メソッドに相当するものを探しています。

私はカスタムコンバーターを書くことができることを知っていますが、それはこれにはやり過ぎのようです。

編集:

ここにもう少しコンテキストがあります。この背後にある理由は、私のクラスが次のようになっていることです。

public class Address : IAddress
{
    /// <summary>
    /// Gets or sets the two character country code
    /// </summary>
    [JsonProperty("countryCode")]
    [Required]
    public string CountryCode { get; set; }

    /// <summary>
    /// Gets or sets the country code, and province or state code delimited by a vertical pipe: <c>US|MI</c>
    /// </summary>
    [JsonProperty("countryProvinceState")]
    public string CountryProvinceState
    {
        get
        {
            return string.Format("{0}|{1}", this.CountryCode, this.ProvinceState);
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(value) && value.Contains("|"))
            {
                string[] valueParts = value.Split('|');
                if (valueParts.Length == 2)
                {
                    this.CountryCode = valueParts[0];
                    this.ProvinceState = valueParts[1];
                }
            }
        }
    }

    [JsonProperty("provinceState")]
    [Required]
    public string ProvinceState { get; set; }
}

リクエストにはCountryProvinceStateプロパティが必要ですが、逆シリアル化してセッターロジックをトリガーしたくありません。

15
RJ Cuthbertson

最も簡単な方法は、不動産を [JsonIgnore] そしてget-onlyプロキシプロパティを作成します:

    /// <summary>
    /// Gets or sets the country code, and province or state code delimited by a vertical pipe: <c>US|MI</c>
    /// </summary>
    [JsonIgnore]
    public string CountryProvinceState
    {
        get
        {
            return string.Format("{0}|{1}", this.CountryCode, this.ProvinceState);
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(value) && value.Contains("|"))
            {
                string[] valueParts = value.Split('|');
                if (valueParts.Length == 2)
                {
                    this.CountryCode = valueParts[0];
                    this.ProvinceState = valueParts[1];
                }
            }
        }
    }

    [JsonProperty("countryProvinceState")]
    string ReadCountryProvinceState
    {
        get { return CountryProvinceState; } 
    }

必要に応じて、プロキシプロパティをプライベートにすることができます。

更新

多くのクラスの多くのプロパティに対してこれを行う必要がある場合は、カスタム属性をチェックする独自の ContractResolver を作成する方が簡単な場合があります。見つかった場合、属性はプロパティがget-onlyであることを示します。

[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class GetOnlyJsonPropertyAttribute : Attribute
{
}

public class GetOnlyContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property != null && property.Writable)
        {
            var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
            if (attributes != null && attributes.Count > 0)
                property.Writable = false;
        }
        return property;
    }
}

次に、次のように使用します。

[JsonProperty("countryProvinceState")]
[GetOnlyJsonProperty]
public string CountryProvinceState { get; set; }

その後:

        var settings = new JsonSerializerSettings { ContractResolver = new GetOnlyContractResolver() };

        var address = JsonConvert.DeserializeObject<Address>(jsonString, settings);
17
dbc

あなたの質問では、単純な文字列プロパティがあります。ただし、オブジェクトがある場合は少し複雑になります。デシリアライズはオブジェクトのプロパティに移動するため、.Writeable = falseを使用したソリューションは機能しません。次のコードについて考えてみます。

public class Constants
{
    public Address Headquarters { get; set; }

    public static Constants Instance = new Constants
    {
        Headquarters = new Address { Street = "Baker Street" }
    };
}
public class Address
{
    public string Street { get; set; }
}

public class Data
{
    [GetOnlyJsonProperty]
    // we want this to be included in the response, but not deserialized back
    public Address HqAddress { get { return Constants.Instance.Headquarters; } }
}

// somewhere in your code:
var data = JsonConvert.DeserializeObject<Data>("{'HqAddress':{'Street':'Liverpool Street'}}", settings);

これで、JSONはgetterしかないため、Addreessプロパティの新しいHqAddressオブジェクトを作成しようとしません。ただし、(.Writeable == falseであっても)さらに深くなり、Streetプロパティを逆シリアル化し、「LiverpoolStreet」をConstants.Instance.Heqdquartersオブジェクトに設定し、アプリケーションの定数のデータを上書きします。

解決策は:Newtonsoft.JSONの新しいバージョン(v10で試しました)には、新しいプロパティShouldDeserializeがあります。したがって、リゾルバは次のようになります。

public class GetOnlyContractResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var property = base.CreateProperty(member, memberSerialization);
            if (property != null) // Change here (1)
            {
                var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
                if (attributes != null && attributes.Count > 0)
                    property.ShouldDeserialize = (a) => false;  // Change here (2)
            }
            return property;
        }
    }

(1)&& property.Writeableの条件を削除したので、HqAddressを処理し、完全なツリーの逆シリアル化をスキップします。 (2)ShouldDeserializeは述語であり、すべてのオブジェクトで逆シリアル化するために呼び出されます。したがって、条件付きで一部のプロパティのみをスキップできます。しかし、ここでは、たとえば簡単にしました。

1
Pavlo Lissov