使用JSON.NET为对象序列化动态属性名称

我正在使用JSON.NET来序列化我的对象以连接到REST API。 我的对象中需要序列化为JSON的属性之一具有动态属性名称。 如果此属性的struct中包含的值是数值,则JSON属性为“type_id”,但如果此值为字符串值,则JSON属性名称为“type_code”。 我尝试使用自定义JsonConverter ,但是当我尝试序列化时,我收到带有此消息的JsonWriterException

“状态属性中的Token PropertyName将导致无效的JSON对象。路径”。”

下面是我的对象的子集,如下所示我没有在我的对象中指定属性名称,因为:

 [JsonProperty("title",Required=Required.Always,Order=1)] public string Title { get; set; } [JsonProperty("date",Order=3)] [JsonConverter(typeof(IsoDateTimeConverter))] public DateTime Date { get; set; } [JsonProperty(Order=2)] [JsonConverter(typeof(TypeIdentifierJsonConverter))] public TypeIdentifier DocTypeIdentifier { get; set; } 

在TypeIdentifier类中,我在WriteJson()方法中有以下内容:

 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { TypeIdentifier docTypeId; id= (TypeIdentifier) value; writer.WritePropertyName(id.ParameterName); writer.WriteValue(id.Value); } 

但是,我假设它默认为对象属性的名称而不是我的自定义属性,导致JSON字符串中的单个值的两个属性名称。 如何为此动态设置属性名称,因为JsonPropertyAttribute标记在未明确指定时会显示拉取对象的属性名称?

注意:此对象永远不需要从此应用程序反序列化。

编辑:此对象使用[JsonObject(MemberSerialization.OptIn)]属性进行标记

JsonConverter无法在父对象中设置属性的名称。 当调用转换器的WriteJson方法时,属性名称已经写入JSON; 作者期待只有一个值。 这就是你收到错误的原因。 为了使其工作,必须为父对象创建自定义转换器。 然后,该转换器将负责编写其子项的属性名称和值。

跟进

可以为父对象编写转换器,以便仍然遵守应用于它的JSON属性,同时仍然可以实现所需的结果。 我将概述下面的方法。

首先,一点点设置。 既然你没有说出你的类叫什么,我会假设这个例子叫做Document 。 我们只需对其进行一次实质性更改,即从DocTypeIdentifier属性中删除[JsonConverter]属性。 所以我们有:

 [JsonObject(MemberSerialization.OptIn)] class Document { [JsonProperty("title", Required = Required.Always, Order = 1)] public string Title { get; set; } [JsonProperty("date", Order = 3)] [JsonConverter(typeof(IsoDateTimeConverter))] public DateTime Date { get; set; } [JsonProperty(Order = 2)] public TypeIdentifier DocTypeIdentifier { get; set; } public string OtherStuff { get; set; } } 

你也没有显示TypeIdentifier类的代码,所以我假设它看起来像这样,例如:

 class TypeIdentifier { public string Value { get; set; } public string ParameterName { get; set; } } 

有了这个,我们可以制作转换器。 这种方法相当简单:我们将Document加载到JObject ,利用它尊重应用的属性这一事实,然后返回并修复DocTypeIdentifier的序列化,因为它需要特殊处理。 一旦我们有了,我们将JObject写出来给JsonWriter 。 这是代码:

 class DocumentConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(Document)); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Document doc = (Document)value; // Create a JObject from the document, respecting existing JSON attribs JObject jo = JObject.FromObject(value); // At this point the DocTypeIdentifier is not serialized correctly. // Fix it by replacing the property with the correct name and value. JProperty prop = jo.Children() .Where(p => p.Name == "DocTypeIdentifier") .First(); prop.AddAfterSelf(new JProperty(doc.DocTypeIdentifier.ParameterName, doc.DocTypeIdentifier.Value)); prop.Remove(); // Write out the JSON jo.WriteTo(writer); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } } 

现在我们有了转换器,但是我们不能简单地使用[JsonConverter]属性来装饰Document类以便使用它。 如果我们这样做,我们最终会得到一个递归循环,因为当我们将文档加载到JObject时,转换器会尝试使用它自己。 因此,我们需要创建转换器的实例并通过设置将其传递给序列化器。 转换器的CanConvert方法确保它在正确的类上使用。 JObject.FromObject方法在内部使用不同的序列化程序实例,因此它不会看到DocumentConverter ,因此不会遇到麻烦。

 JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new DocumentConverter()); string json = JsonConvert.SerializeObject(doc, settings); 

这是一个演示转换器的演示:

 class Program { static void Main(string[] args) { Document doc = new Document { Title = "How to write a JSON converter", Date = DateTime.Today, DocTypeIdentifier = new TypeIdentifier { ParameterName = "type_id", Value = "26" }, OtherStuff = "this should not appear in the JSON" }; JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new DocumentConverter()); settings.Formatting = Formatting.Indented; string json = JsonConvert.SerializeObject(doc, settings); Console.WriteLine(json); } } 

以下是上述输出:

 { "title": "How to write a JSON converter", "type_id": "26", "date": "2014-03-28T00:00:00-05:00" }