Json.NET 不会使用 JSONPaths 作为开箱即用的属性名称来序列化对象,因为它是基于协定的序列化程序。因此,它将以相同的方式序列化某种类型的所有实例T,无论它们在序列化图中遇到什么。相反,您希望为遇到的每个实例创建一个稍微不同的合约,以反映图中实例的路径。
那么,您有哪些选择来获取所需的 JSON?
首先,您可以序列化为JObject,然后使用public static void Rename(this JToken token, string newName) 扩展方法将其从this answer 由Brian Rogers 后处理到Rename JProperty in json.net:
var person = new Person
{
// Initialize your Person as required
};
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var jObject = JObject.FromObject(person, JsonSerializer.CreateDefault(settings));
foreach (var item in jObject.Descendants().OfType<JProperty>().Select(p => (Property: p, p.Path)).ToList())
{
// The Rename extension method from https://stackoverflow.com/a/47269811/3744182
item.Property.Rename("person." + item.Path);
}
var json = jObject.ToString();
演示小提琴 #1 here.
其次,您可以在序列化时通过继承JsonTextWriter 并覆盖WritePropertyName() 方法来动态重命名属性:
public class NameRemappingJsonWriter : JsonTextWriter
{
readonly Action<string> jsonWriterWritePropertyName;
readonly Func<string, int, string, string> map;
public NameRemappingJsonWriter(TextWriter textWriter, Func<string, int, string, string> map) : base(textWriter)
{
this.map = map ?? throw new ArgumentNullException(nameof(map));
//Method to call a base-of-base-class method taken from this answer https://stackoverflow.com/a/32562464
//By https://stackoverflow.com/users/5311735/evk
//To https://stackoverflow.com/questions/2323401/how-to-call-base-base-method
var ptr = typeof(JsonWriter).GetMethod("WritePropertyName", new[] { typeof(string) }).MethodHandle.GetFunctionPointer();
jsonWriterWritePropertyName = (Action<string>)Activator.CreateInstance(typeof(Action<string>), this, ptr);
}
public override void WritePropertyName(string name) => WritePropertyName(name, true);
public override void WritePropertyName(string name, bool escape)
{
jsonWriterWritePropertyName(name);
WriteRaw(JsonConvert.ToString(map(name, Top, Path)));
WriteRaw(":");
}
}
然后:
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
Func<string, int, string, string> map = (name, depth, parentPath) => "person." + parentPath;
using var textWriter = new StringWriter();
using (var jsonWriter = new NameRemappingJsonWriter(textWriter, map) { Formatting = Formatting.Indented })
{
JsonSerializer.CreateDefault(settings).Serialize(jsonWriter, person);
}
var json = textWriter.ToString();
请注意,要让它工作,我必须调用基类的基类 JsonWriter.WritePropertyName() 方法,这有点粗略和 not recommended or supported natively in c#。
演示小提琴 #2 here.