37

このような単純なクラス/インターフェースで

public interface IThing
{
    string Name { get; set; }
}

public class Thing : IThing
{
    public int Id { get; set; }
    public string Name { get; set; }
}

「名前」プロパティのみ (基になるインターフェイスのプロパティのみ) で JSON 文字列を取得するにはどうすればよいですか?

実際、私がそれを作るとき:

var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);

完全なオブジェクトを JSON (ID + 名前) として取得します。

4

9 に答える 9

27

私が使っている方法は、

public class InterfaceContractResolver : DefaultContractResolver
{
    private readonly Type _InterfaceType;
    public InterfaceContractResolver (Type InterfaceType)
    {
        _InterfaceType = InterfaceType;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        //IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
        return properties;
    }
}

// To serialize do this:
var settings = new JsonSerializerSettings() {
     ContractResolver = new InterfaceContractResolver (typeof(IThing))
});
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);
于 2014-01-05T02:23:17.173 に答える
21

ネストされたインターフェイス + xsd.exe オブジェクトのサポートを備えた改良版

ここでさらに別のバリエーション。コードはhttp://www.tomdupont.net/2015/09/how-to-only-serialize-interface.htmlからのもので、ここの他の回答よりも次の点が改善されています

  • 階層を処理するため、Interface2[]内にがある場合Interface1はシリアル化されます。
  • WCF プロキシ オブジェクトをシリアル化しようとしたところ、結果の JSON が{}. すべてのプロパティがに設定されていることが判明したIgnore=trueため、すべてを無視しないように設定するループを追加する必要がありました。

    public class InterfaceContractResolver : DefaultContractResolver
    {
        private readonly Type[] _interfaceTypes;
    
        private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
    
        public InterfaceContractResolver(params Type[] interfaceTypes)
        {
            _interfaceTypes = interfaceTypes;
    
            _typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
        }
    
        protected override IList<JsonProperty> CreateProperties(
            Type type,
            MemberSerialization memberSerialization)
        {
            var typeToSerialize = _typeToSerializeMap.GetOrAdd(
                type,
                t => _interfaceTypes.FirstOrDefault(
                    it => it.IsAssignableFrom(t)) ?? t);
    
            var props = base.CreateProperties(typeToSerialize, memberSerialization);
    
            // mark all props as not ignored
            foreach (var prop in props)
            {
                prop.Ignored = false;
            }
    
            return props;
        }
    }
    
于 2017-01-13T04:39:31.437 に答える
7

注釈を追加して[JsonIgnore]、属性を無視できます。

于 2013-06-15T12:56:37.830 に答える
3

このタスクに直面したときに私たちが最終的に何をしたかを共有したいと思います. OPのインターフェースとクラスを考えると...

public interface IThing
{
    string Name { get; set; }
}

public class Thing : IThing
{
   public int Id { get; set; }
   public string Name { get; set; }
}

...インターフェイスの直接実装であるクラスを作成しました...

public class DirectThing : IThing
{
   public string Name { get; set; }
}

次に、単にThingインスタンスをシリアライズし、 としてデシリアライズしてから、 としてDirectThingシリアライズしましたDirectThing:

var thing = new Thing();
JsonConvert.SerializeObject(
    JsonConvert.DeserializeObject<DirectThing>(JsonConvert.SerializeObject(thing)));

このアプローチは、長いインターフェイス継承チェーンで機能します...DirectThing関心のあるレベルで直接クラス (この例では) を作成するだけです。反射や属性を気にする必要はありません。

メンテナンスの観点からは、メンバーを に追加すると、DirectThingクラスのメンテナンスが容易になります。これは、メンバーも に追加しIThingていないとコンパイラーがエラーを出すためですDirectThing。ただし、メンバー Xを削除して代わりIThing 配置するThing場合は、忘れずに削除する必要がありますDirectThing。そうしないと、最終結果に X が含まれてしまいます。

パフォーマンスの観点から、ここでは 1 つではなく 3 つの (デ) シリアル化操作が行われているため、状況に応じて、リフレクター/属性ベースのソリューションとこのソリューションのパフォーマンスの違いを評価することをお勧めします。私の場合、これを小規模で行っていたので、数マイクロ/ミリ秒の潜在的な損失については心配していませんでした。

それが誰かを助けることを願っています!

于 2018-01-19T15:49:07.377 に答える
2

最後に、それが機能しないときが来ました...別の複雑なオブジェクトの中に入れたい場合、それは適切にシリアル化されません。

そのため、特定のアセンブリに格納されたデータと、同じ基本インターフェイスを持つ型のデータのみを抽出するバージョンを作成しました。

なので、.Net Core JsonContractResolver として作られています。

データ抽出に加えて、次のことを解決します:
a) データをクライアントに送信する前にキャメルケース変換
b) 許可されたスコープ (アセンブリによって) から最上位のインターフェイスを使用 c) フィールドの順序を修正: ほとんどの基本クラスのフィールドが最初にリストされ、ネストされたオブジェクトはこのルールも満たしています。

public class OutputJsonResolver : DefaultContractResolver
{
    #region Static Members
    private static readonly object syncTargets = new object();
    private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();

    private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
    #endregion

    #region Override Members
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        if (type.Assembly != OutputJsonResolver.CommonAssembly)
            return base.CreateProperties(type, memberSerialization);

        IList<JsonProperty> properties;
        if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
        {
            lock (OutputJsonResolver.syncTargets)
            {
                if (OutputJsonResolver.Targets.ContainsKey(type) == false)
                {
                    properties = this.CreateCustomProperties(type, memberSerialization);

                    OutputJsonResolver.Targets[type] = properties;
                }
            }
        }

        return properties;
    }
    protected override string ResolvePropertyName(string propertyName)
    {
        return propertyName.ToCase(Casing.Camel);
    }
    #endregion

    #region Assistants
    private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
    {
        // Hierarchy
        IReadOnlyList<Type> types = this.GetTypes(type);

        // Head
        Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();

        // Sources
        IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);

        // Targets
        IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);

        // Repository
        IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);

        foreach (Type current in types.Reverse())
        {
            IReadOnlyPage<JsonProperty> page;
            if (repository.TryGetValue(current, out page) == true)
                targets.AddRange(page);
        }

        return targets;
    }
    private IReadOnlyList<Type> GetTypes(Type type)
    {
        List<Type> types = new List<Type>();

        if (type.IsInterface == true)
            types.Add(type);

        types.AddRange(type.GetInterfaces());

        return types;
    }
    #endregion
}
于 2016-06-23T01:04:05.000 に答える