sql >> Databáze >  >> NoSQL >> MongoDB

JSON .NET Custom Name Resolver pro dílčí vlastnosti

Co můžete udělat, je vytvořit pro danou vlastnost vlastní JsonConverter která serializuje příslušnou hodnotu vlastnosti pomocí jiného JsonSerializer vytvořené pomocí jiného nástroje pro řešení smluv, například takto:

public class AlternateContractResolverConverter : JsonConverter
{
    [ThreadStatic]
    static Stack<Type> contractResolverTypeStack;

    static Stack<Type> ContractResolverTypeStack { get { return contractResolverTypeStack = (contractResolverTypeStack ?? new Stack<Type>()); } }

    readonly IContractResolver resolver;

    JsonSerializerSettings ExtractAndOverrideSettings(JsonSerializer serializer)
    {
        var settings = serializer.ExtractSettings();
        settings.ContractResolver = resolver;
        settings.CheckAdditionalContent = false;
        if (settings.PreserveReferencesHandling != PreserveReferencesHandling.None)
        {
            // Log an error throw an exception?
            Debug.WriteLine(string.Format("PreserveReferencesHandling.{0} not supported", serializer.PreserveReferencesHandling));
        }
        return settings;
    }

    public AlternateContractResolverConverter(Type resolverType)
    {
        if (resolverType == null)
            throw new ArgumentNullException("resolverType");
        resolver = (IContractResolver)Activator.CreateInstance(resolverType);
        if (resolver == null)
            throw new ArgumentNullException(string.Format("Resolver type {0} not found", resolverType));
    }

    public override bool CanRead { get { return ContractResolverTypeStack.Count == 0 || ContractResolverTypeStack.Peek() != resolver.GetType(); } }
    public override bool CanWrite { get { return ContractResolverTypeStack.Count == 0 || ContractResolverTypeStack.Peek() != resolver.GetType(); } }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException("This contract resolver is intended to be applied directly with [JsonConverter(typeof(AlternateContractResolverConverter), typeof(SomeContractResolver))] or [JsonProperty(ItemConverterType = typeof(AlternateContractResolverConverter), ItemConverterParameters = ...)]");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        using (ContractResolverTypeStack.PushUsing(resolver.GetType()))
            return JsonSerializer.CreateDefault(ExtractAndOverrideSettings(serializer)).Deserialize(reader, objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        using (ContractResolverTypeStack.PushUsing(resolver.GetType()))
            JsonSerializer.CreateDefault(ExtractAndOverrideSettings(serializer)).Serialize(writer, value);
    }
}

internal static class JsonSerializerExtensions
{
    public static JsonSerializerSettings ExtractSettings(this JsonSerializer serializer)
    {
        // There is no built-in API to extract the settings from a JsonSerializer back into JsonSerializerSettings,
        // so we have to fake it here.
        if (serializer == null)
            throw new ArgumentNullException("serializer");
        var settings = new JsonSerializerSettings
        {
            CheckAdditionalContent = serializer.CheckAdditionalContent,
            ConstructorHandling = serializer.ConstructorHandling,
            ContractResolver = serializer.ContractResolver,
            Converters = serializer.Converters,
            Context = serializer.Context,
            Culture = serializer.Culture,
            DateFormatHandling = serializer.DateFormatHandling,
            DateFormatString = serializer.DateFormatString,
            DateParseHandling = serializer.DateParseHandling,
            DateTimeZoneHandling = serializer.DateTimeZoneHandling,
            DefaultValueHandling = serializer.DefaultValueHandling,
            EqualityComparer = serializer.EqualityComparer,
            // No Get access to the error event, so it cannot be copied.
            // Error = += serializer.Error
            FloatFormatHandling = serializer.FloatFormatHandling,
            FloatParseHandling = serializer.FloatParseHandling,
            Formatting = serializer.Formatting,
            MaxDepth = serializer.MaxDepth,
            MetadataPropertyHandling = serializer.MetadataPropertyHandling,
            MissingMemberHandling = serializer.MissingMemberHandling,
            NullValueHandling = serializer.NullValueHandling,
            ObjectCreationHandling = serializer.ObjectCreationHandling,
            ReferenceLoopHandling = serializer.ReferenceLoopHandling,
            // Copying the reference resolver doesn't work in the default case, since the
            // actual BidirectionalDictionary<string, object> mappings are held in the 
            // JsonSerializerInternalBase.
            // See https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs
            ReferenceResolverProvider = () => serializer.ReferenceResolver,
            PreserveReferencesHandling = serializer.PreserveReferencesHandling,
            StringEscapeHandling = serializer.StringEscapeHandling,
            TraceWriter = serializer.TraceWriter,
            TypeNameHandling = serializer.TypeNameHandling,
            // Changes in Json.NET 10.0.1
            //TypeNameAssemblyFormat was obsoleted and replaced with TypeNameAssemblyFormatHandling in Json.NET 10.0.1
            //TypeNameAssemblyFormat = serializer.TypeNameAssemblyFormat,
            TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling,
            //Binder was obsoleted and replaced with SerializationBinder in Json.NET 10.0.1
            //Binder = serializer.Binder,
            SerializationBinder = serializer.SerializationBinder,
        };
        return settings;
    }
}

public static class StackExtensions
{
    public struct PushValue<T> : IDisposable
    {
        readonly Stack<T> stack;

        public PushValue(T value, Stack<T> stack)
        {
            this.stack = stack;
            stack.Push(value);
        }

        // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
        public void Dispose()
        {
            if (stack != null)
                stack.Pop();
        }
    }

    public static PushValue<T> PushUsing<T>(this Stack<T> stack, T value)
    {
        if (stack == null)
            throw new ArgumentNullException();
        return new PushValue<T>(value, stack);
    }
}

Pak jej použijte takto:

public class RootObject
{
    public string Name { get; set; }
    public DateTime DateCreated { get; set; }

    [JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy))]
    [JsonConverter(typeof(AlternateContractResolverConverter), typeof(DefaultContractResolver))]
    public SomeDocument SomeDocument { get; set; }
}

public class SomeDocument
{
    public string MyFirstProperty { get; set; }
    public string mysecondPROPERTY { get; set; }
    public AnotherRandomSubdoc another_random_subdoc { get; set; }
}

public class AnotherRandomSubdoc
{
    public string evenmoredata { get; set; }
    public DateTime DateCreated { get; set; }
}

(Předpokládám, že chcete "SomeDocument" název vlastnosti bude serializován doslovně, i když to z vaší otázky nebylo zcela jasné. K tomu používám JsonPropertyAttribute.NamingStrategyType> z Json.NET 9.0.1. Pokud používáte starší verzi, budete muset explicitně nastavit název vlastnosti.)

Výsledný JSON pak bude:

{
  "name": "Question 40597532",
  "dateCreated": "2016-11-14T05:00:00Z",
  "SomeDocument": {
    "MyFirstProperty": "my first property",
    "mysecondPROPERTY": "my second property",
    "another_random_subdoc": {
      "evenmoredata": "even more data",
      "DateCreated": "2016-11-14T05:00:00Z"
    }
  }
}

Upozorňujeme, že toto řešení NEFUNGUJE dobře s zachováním odkazů na objekty . Pokud potřebujete, aby spolupracovaly, možná budete muset zvážit přístup založený na zásobníku podobný přístupu z Json.NET serializovat podle hloubky a atributu

Ukázkové housle zde .

Mimochodem, uvažovali jste o uložení tohoto JSON jako nezpracovaného řetězcového literálu, jako v odpovědi na tato otázka ?



  1. MongoDB nemůže aktualizovat dokument, protože _id je řetězec, nikoli ObjectId

  2. provádět matematiku ve vyhledávacích dotazech mongodb

  3. Konfigurace MongoDB v CakePHP 3.x

  4. Jak uložit binární objekt v redis pomocí uzlu?