2014-03-28 85 views
13

我使用JSON.NET对我的对象进行序列化以连接到REST API。我的对象中需要序列化为JSON的其中一个属性具有动态属性名称。 如果此属性的结构中包含的值是数值,那么JSON属性是“type_id”,但是如果此值是字符串值,那么JSON属性名称是“type_code”。我试图使用自定义JsonConverter这一点,但我得到这个消息的JsonWriterException当我试图序列:序列化使用JSON.NET的对象的动态属性名称

“令牌属性名国家财产会导致一个无效的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)]属性

+0

你有对象上的[Serializable]属性? – Robert

+0

@Robert使用JSON.NET不是内置的.NET序列化,意思是那些被忽略。不过,我确实有为该类设置的[JsonObject(MemberSerialization.OptIn)]属性。 – JNYRanger

回答

21

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<JProperty>() 
          .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" 
} 
+0

所以基本上我需要为整个对象编写一个转换器,并且使用用于自动序列化的属性不会在这里执行这个技巧?由于我已经有了几个用于内部对象的转换器,当我开始使用这些字段时,是否可以从父转换器调用它们,因此我不需要重写这些转换器? – JNYRanger

+1

有一种方法可以编写转换器,以使属性的属性仍然受到尊重(意味着其他转换器应该自动调用),但是,您不能在父项中放置一个'[JsonConverter]'属性对象,否则在转换器尝试使用它时会导致递归循环。给我几分钟,我会试着举个例子来演示。 –

+0

非常感谢。一个例子会非常有帮助,我一直在对墙进行研究,并对DefaultContractResolver进行了扩展,似乎并不符合我的需求,我也不想在第一个中使用那么多的反射地点。 – JNYRanger

相关问题