gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 10:02:46 32 4
gpt4 key购买 nike

我有一个从 MongoDB 返回 JSON 对象的 API,其中一个属性是“开放式”文档,这意味着它可以是该属性的任何有效 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”的完全匹配,并且仍然会驼峰式排列所有子属性。

最佳答案

你可以做的是,对于有问题的属性,创建一个 custom JsonConverter,它使用由不同的契约(Contract)解析器创建的不同的 JsonSerializer 序列化有问题的属性值,如下所示:

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);
}
}

然后像这样使用它:

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

(这里我假设您希望逐字序列化 "SomeDocument" 属性名称,即使您的问题并不完全清楚。为此,我使用 JsonPropertyAttribute.NamingStrategyType 来自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"
}
}
}

请注意,此解决方案不适用于 preserving object references 。如果您需要它们一起工作,您可能需要考虑一种基于堆栈的方法,类似于 Json.NET serialize by depth and attribute 中的方法

演示 fiddle here

顺便说一句,您是否考虑过将此 JSON 存储为原始字符串文字,如 this question 的答案?

关于c# - 子属性的 JSON .NET 自定义名称解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40597532/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com