2017-05-06 63 views
1

我的要求是在反序列化過程中使用JsonProperty並在序列化過程中忽略JsonProperty。我的模型,使用自定義JsonConverter和自定義ContractResolver時StackOverflow異常

[JsonConverter(typeof(JsonPathConverter))] 
public class FacebookFeed 
{ 
    public FacebookFeed() 
    { 
     Posts = new List<FacebookFeedPost>(); 
    } 

    [JsonProperty("name")] 
    public string Name { get; set; } 

    [JsonProperty("fan_count")] 
    public int Likes { get; set; } 

    [JsonProperty("feed.data")] 
    public List<FacebookFeedPost> Posts { get; set; } 
} 

public class FacebookFeedPost 
{ 
    [JsonProperty("id")] 
    public string Id { get; set; } 

    [JsonProperty("message")] 
    public string Message { get; set; } 

    [JsonProperty("created_time")] 
    public DateTime Date { get; set; } 

    [JsonProperty("comments.summary.total_count")] 
    public int Comments { get; set; }   
} 

class IgnoreJsonPropertyContractResolver : DefaultContractResolver 
{ 
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) 
    { 
     var list = base.CreateProperties(type, memberSerialization); 
     foreach (JsonProperty prop in list) 
     { 
      prop.PropertyName = prop.UnderlyingName; 
     } 
     return list; 
    } 
} 

public class JsonPathConverter : JsonConverter 
{ 
    public override object ReadJson(JsonReader reader, Type objectType, 
            object existingValue, JsonSerializer serializer) 
    { 
     var jo = JObject.Load(reader); 
     object targetObj = Activator.CreateInstance(objectType); 

     foreach (var prop in objectType.GetProperties() 
               .Where(p => p.CanRead && p.CanWrite)) 
     { 
      var att = prop.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault(); 
      var jsonPath = (att != null ? att.PropertyName : prop.Name); 
      var token = jo.SelectToken(jsonPath); 
      if (token != null && token.Type != JTokenType.Null) 
      { 
       var value = token.ToObject(prop.PropertyType, serializer); 
       prop.SetValue(targetObj, value, null); 
      } 
     } 

     return targetObj; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var s = new JsonSerializer(); 
     serializer.ContractResolver = new IgnoreJsonPropertyContractResolver(); 
     var t = JToken.FromObject(value, s); 
     t.WriteTo(writer); 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     // CanConvert is not called when [JsonConverter] attribute is used 
     return false; 
    } 

    public override bool CanWrite 
    { 
     get { return true; } 
    } 

} 

問題是WriteJson。請注意,我使用內部序列化我的對象的ASP.NET Web Api,這就是爲什麼我使用JsonConverter。我不想改變全球網絡API設置。

+0

哪裏是異常提出的準確?你有調用堆棧嗎? – themiurge

+0

'var t = JToken.FromObject(value,s);'在這一行。無堆棧跟蹤。 – user960567

+0

你的問題是,轉換器正在遞歸調用自己。由於它直接應用於該類型,對JToken.FromObject(value,s);的內部調用將調用轉換器第二個實例的WriteJson()。 [JSON.Net在使用[JsonConvert()]'](https://stackoverflow.com/q/29719509/3744182)時拋出StackOverflowException異常可以避免出現這種情況。 – dbc

回答

2

我想你有一個XY Problem去這裏。

我想你要解決的真正問題是你有一些深層嵌套的JSON,你想反序列化成一個更簡單的扁平模型,然後你想能夠將這個更簡單的模型序列化爲JSON。

對於反序列化部分,您找到了a possible solution,該轉換器將[JsonProperty]屬性重載爲接受每個屬性的路徑,使其更容易平坦化爲更簡單的模型。您已使用[JsonConverter]屬性將轉換器應用於您的課程,因爲您不想修改Web API的全局設置。

但是現在,當您序列化時,名稱(路徑)將由序列化程序獲取,您不需要。所以你需要一種方法來忽略它們。然後,您發現a possible solution,涉及使用自定義合約解析程序將[JsonProperty]名稱恢復爲其原始值。您試圖在轉換器的WriteJson方法中應用解析器,但是當您試圖在轉換器內序列化對象時,轉換器將被遞歸調用,因爲模型類具有[JsonConverter]屬性。所以現在你被困住了,你在問怎麼解決這個最新的問題。我到目前爲止是否正確?好吧,讓我們回過頭幾步解決真正的問題。您想要將深度嵌套的JSON反序列化爲簡單模型,然後將該簡單模型序列化爲JSON。我認爲你在正確的軌道上使用轉換器在反序列化中壓扁JSON,但沒有理由轉換器必須劫持[JsonProperty]屬性才能做到這一點。它可以對JSON路徑使用自己的自定義屬性,以免干擾串行器的正常操作。如果你這樣做,那麼你可以讓轉換器的CanWrite方法返回false,這將導致序列化程序忽略轉換器並使用默認的屬性名稱,這是您首先想要的。

所以,這裏是你需要做的:

首先,做一個自定義屬性類中使用的屬性路徑:

public class JsonPathAttribute : Attribute 
{ 
    public JsonPathAttribute(string jsonPath) 
    { 
     JsonPath = jsonPath; 
    } 

    public string JsonPath { get; set; } 
} 

其次,改變你的轉換器的ReadJson方法看爲此新屬性而不是[JsonProperty],並使CanWrite方法返回false。你也可以去掉WriteJson方法的實現,因爲它永遠不會被調用。自定義解析器類也不是必需的。

public class JsonPathConverter : JsonConverter 
{ 
    public override object ReadJson(JsonReader reader, Type objectType, 
            object existingValue, JsonSerializer serializer) 
    { 
     var jo = JObject.Load(reader); 
     object targetObj = Activator.CreateInstance(objectType); 

     foreach (var prop in objectType.GetProperties() 
             .Where(p => p.CanRead && p.CanWrite)) 
     { 
      var att = prop.GetCustomAttributes(true) 
          .OfType<JsonPathAttribute>() 
          .FirstOrDefault(); 
      var jsonPath = (att != null ? att.JsonPath : prop.Name); 
      var token = jo.SelectToken(jsonPath); 
      if (token != null && token.Type != JTokenType.Null) 
      { 
       var value = token.ToObject(prop.PropertyType, serializer); 
       prop.SetValue(targetObj, value, null); 
      } 
     } 

     return targetObj; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     // WriteJson is not called when CanWrite returns false 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     // CanConvert is not called when [JsonConverter] attribute is used 
     return false; 
    } 
} 

最後,將模型類更改爲使用新屬性。請注意,您還需要使用[JsonConverter]屬性標記這兩個類;在你的問題中,你只標記了第一個。

[JsonConverter(typeof(JsonPathConverter))] 
public class FacebookFeed 
{ 
    public FacebookFeed() 
    { 
     Posts = new List<FacebookFeedPost>(); 
    } 

    [JsonPath("name")] 
    public string Name { get; set; } 

    [JsonPath("fan_count")] 
    public int Likes { get; set; } 

    [JsonPath("feed.data")] 
    public List<FacebookFeedPost> Posts { get; set; } 
} 

[JsonConverter(typeof(JsonPathConverter))] 
public class FacebookFeedPost 
{ 
    [JsonPath("id")] 
    public string Id { get; set; } 

    [JsonPath("message")] 
    public string Message { get; set; } 

    [JsonPath("created_time")] 
    public DateTime Date { get; set; } 

    [JsonPath("comments.summary.total_count")] 
    public int Comments { get; set; } 
} 

就是這樣。它現在應該按照你想要的方式工作。

演示提琴:https://dotnetfiddle.net/LPPAmH

+0

謝謝。我認爲你是對的我有XY問題:) – user960567