C# 子属性的JSON.NET自定义名称解析器

C# 子属性的JSON.NET自定义名称解析器,c#,json,mongodb,json.net,C#,Json,Mongodb,Json.net,我有一个API,它从MongoDB返回一个JSON对象,其中一个属性是“开放式”文档,这意味着它可以是该属性的任何有效JSON。我不知道这些属性的名称是什么,它们可以是任何字符串。我只知道这个特定的属性需要被序列化,它在数据库中的存储方式。因此,如果最初存储的属性名是“Someproperty”,JSON中的序列化响应需要是“Someproperty”,而不是“Someproperty” 我们有以下配置: ContractResolver = new CamelCasePropertyNames

我有一个API,它从MongoDB返回一个JSON对象,其中一个属性是“开放式”文档,这意味着它可以是该属性的任何有效JSON。我不知道这些属性的名称是什么,它们可以是任何字符串。我只知道这个特定的属性需要被序列化,它在数据库中的存储方式。因此,如果最初存储的属性名是“Someproperty”,JSON中的序列化响应需要是“Someproperty”,而不是“Someproperty”

我们有以下配置:

ContractResolver = new CamelCasePropertyNamesContractResolver();
在我们的CustomJsonSerializer中,但返回“开放式”JSON时会弄乱响应的格式。事实上,我们希望响应完全是它们在MongoDB(BSON)中的存储方式,但我们需要对所有这些属性进行骆驼式封装。我知道在通过数据库存储/检索时,这些值会保持其大小写,所以这不是问题所在

如何让JSON.net基本上绕过CamelCasePropertyNameResolver来处理特定数据点的所有子属性

编辑: 只是想提供更多信息,并分享我已经尝试过的内容:

我考虑过像这样重写PropertyNameResolver:

protected override string ResolvePropertyName(string propertyName)
{
      if (propertyName.ToLower().Equals("somedocument"))
      {
                return propertyName;
      }
      else return base.ResolvePropertyName(propertyName);
}
但是,如果我有这样的JSON结构:

{
   "Name" : "MyObject",
   "DateCreated" : "11/14/2016",
   "SomeDocument" : 
   {
      "MyFirstProperty" : "foo",
      "mysecondPROPERTY" : "bar",
      "another_random_subdoc" : 
      {
         "evenmoredata" : "morestuff"
      }
   }
}

然后,我需要所有的属性和任何子属性的名称保持原样。我发布的上述覆盖(我相信)只会忽略与“somedocument”完全匹配的内容,并且仍然会忽略所有子属性。

我认为您应该向后看

不要试图触摸你不知道的属性,而是让这成为默认行为,触摸你知道的属性


换句话说,不要使用
CamelCasePropertyNamesContractResolver
。适当地处理您知道的属性,并让其他属性透明地通过。

您可以为所讨论的属性创建一个,使用不同的协定解析程序创建的不同协定序列化所讨论的属性值,如下所示:

public class AlternateContractResolverConverter : JsonConverter
{
    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 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)
    {
        return JsonSerializer.CreateDefault(ExtractAndOverrideSettings(serializer)).Deserialize(reader, objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        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
        {
            Binder = serializer.Binder,
            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,
            TypeNameAssemblyFormat = serializer.TypeNameAssemblyFormat,
            TypeNameHandling = serializer.TypeNameHandling,
        };
        return settings;
    }
}
(这里我假设您希望将
“SomeDocument”
属性名逐字序列化,尽管您的问题并不完全清楚。为此,我使用的是from Json.NET 9.0.1。如果您使用的是早期版本,则需要显式设置属性名。)

然后生成的JSON将是:

{
  "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"
    }
  }
}
请注意,此解决方案不适用于。如果需要它们一起工作,您可能需要考虑类似于从

中的堆栈的方法。
顺便说一句,您是否考虑过将此JSON存储为原始字符串文字,如回答中所述?

不幸的是,这是唯一的例外,对于API返回的每个其他数据点,我们都希望使用CamelCased行为(还有许多其他数据点)。只有这个特定属性及其子属性需要遵守非驼峰式的约定。我明白了。无论如何,你根本不可能解释你不知道的事情。得到的反序列化类是什么样子的?如果我理解您是如何使用生成的反序列化对象的,那会有点帮助。相同的类型是否会出现在相关数据点的上方和下方,并且在不同的上下文中需要不同的大小写?除了“SomeDocument”属性之外的所有其他内容都可以用驼峰大小写。例如,我不关心DateCreated。可以响应为“dateCreated”。事实上,“SomeDocument”(根级别的属性名)甚至可以用驼峰式大小写,需要维护其原始大小写的是“MyFirstProperty”、“mysecondPROPERTY”。
{
  "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"
    }
  }
}