it-swarm.com.de

Serialisieren Sie mit Json.net nur Schnittstelleneigenschaften in JSON

Mit einer einfachen Klasse/Schnittstelle wie dieser 

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

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

Wie kann ich den JSON-String nur mit der "Name" -Eigenschaft erhalten (nur den Eigenschaften der zugrunde liegenden Schnittstelle)?

Eigentlich, wenn ich das mache:

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

Ich bekomme das vollständige Objekt als JSON (Id + Name).

28
eka808

Sie können die bedingte Serialisierung verwenden. Schauen Sie sich diese link an. Grundsätzlich müssen Sie die IContractResolver-Schnittstelle implementieren, die ShouldSerialize-Methode überladen und Ihren Resolver an den Konstruktor des Json-Serializers übergeben.

10

Die Methode, die ich verwende,

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

Inspiriert von @ user3161686, hier eine kleine Änderung von InterfaceContractResolver:

public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
        return properties;
    }
}
16
rasx

Verbesserte Version mit verschachtelten Schnittstellen + Unterstützung für xsd.exe-Objekte

Noch eine Variation hier. Der Code stammt aus http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html mit den folgenden Verbesserungen gegenüber anderen Antworten hier

  • Verarbeitet Hierarchien. Wenn Sie also einen Interface2[] in einem Interface1 haben, wird dieser serialisiert.
  • Ich habe versucht, ein WCF-Proxy-Objekt zu serialisieren, und der resultierende JSON wurde als {} bezeichnet. Es stellte sich heraus, dass alle Eigenschaften auf Ignore=true gesetzt waren, also musste ich eine Schleife hinzufügen, damit alle Eigenschaften nicht ignoriert wurden. 

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

Eine Alternative zu [JsonIgnore] sind die Attribute [DataContract] und [DataMember] . Wenn Ihre Klasse mit [DataContract] markiert ist, verarbeitet der Serialisierer nur Eigenschaften, die mit dem [DataMember]-Attribut gekennzeichnet sind (JsonIgnore ist ein "Opt-out" -Modell, während DataContract "op-in" ist).

[DataContract]
public class Thing : IThing
{
    [DataMember]
    public int Id { get; set; }

    public string Name { get; set; }
}

Die Einschränkung beider Ansätze besteht darin, dass sie in der Klasse implementiert werden müssen. Sie können sie nicht zur Schnittstellendefinition hinzufügen. 

6
Marc LaFleur

Sie können die Annotation [JsonIgnore] hinzufügen, um ein Attribut zu ignorieren.

5
monrow

zusätzlich zu der Antwort von @monrow können Sie die Standardeinstellungen [DataContract] und [DataMember] verwenden 

http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reduction-serialized-json-size.aspx

2
Parv Sharma

Ich möchte gerne mitteilen, was wir bei dieser Aufgabe gemacht haben. Angesichts der Schnittstelle und Klasse des OP ...

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

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

... wir haben eine Klasse erstellt, die die direkte Implementierung der Schnittstelle darstellt ...

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

Dann serialisiert einfach unsere Thing-Instanz, deserialisiert sie als DirectThing und serialisiert sie als DirectThing:

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

Dieser Ansatz kann mit einer langen Interface-Vererbungskette arbeiten ... Sie müssen lediglich eine direkte Klasse (in diesem Beispiel DirectThing) auf der interessierenden Ebene erstellen. Sie brauchen sich nicht um Reflexion oder Eigenschaften zu kümmern.

Aus Wartungssicht ist die DirectThing-Klasse einfach zu verwalten, wenn Sie Mitglieder zu IThing hinzufügen, da der Compiler Fehler ausgibt, wenn Sie sie nicht ebenfalls in DirectThing eingefügt haben. Wenn Sie jedoch entfernen ein Mitglied X aus IThing und stattdessen in Thing setzen, müssen Sie daran denken, es aus DirectThing zu entfernen, da sonst X im Endergebnis wäre.

Aus Performance-Sicht finden hier drei (De) -Serialisierungsvorgänge statt eines Vorgangs statt. Abhängig von Ihrer Situation können Sie den Leistungsunterschied von Reflektor-/Attribut-basierten Lösungen im Vergleich zu dieser Lösung bewerten. In meinem Fall habe ich das nur in kleinem Umfang gemacht, also war ich nicht besorgt über mögliche Verluste von einigen Mikro-/Millisekunden.

Hoffe das hilft jemandem!

1
sammy34

Endlich habe ich bekommen, wann es nicht funktioniert ... Wenn Sie ein anderes komplexes Objekt in sich haben möchten, wird es nicht ordnungsgemäß serialisiert.

Ich habe also eine Version erstellt, die nur Daten extrahiert, die in bestimmten Assemblys gespeichert sind, und für Typen, die dieselbe Basisschnittstelle haben.

So wird es als .Net Core JsonContractResolver gemacht.

Zusätzlich zur Datenextraktion löst es:
a) camelCase-Konvertierung vor dem Senden von Daten an den Client
b) verwendet die oberste Schnittstelle aus dem zulässigen Bereich (von Assembly) c) korrigiert die Reihenfolge der Felder: Das Feld der meisten Basisklassen wird zuerst aufgeführt, und verschachtelte Objekte erfüllen diese Regel ebenfalls.

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
}
1
Maxim