2017-05-06 60 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