简体   繁体   English

C# - 在 JSON 序列化和反序列化之间设置不同的 PropertyName

[英]C# - Set different PropertyName between JSON serialization and deserialization

I'm querying an API, the response is a custom JSON. After looking over the web, I managed to deserialize it into a custom object this way:我正在查询 API,响应是自定义 JSON。查看 web 后,我设法通过这种方式将其反序列化为自定义 object:

Post.cs Post.cs

[JsonConverter(typeof(JsonPathConverter))]
public class Post
{
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "taken_at_timestamp")]
    public long Timestamp { get; set; }

    [JsonProperty(PropertyName = "edge_media_preview_like.count")]
    public int LikeCount { get; set; }

    [JsonProperty(PropertyName = "edge_media_to_comment.count")]
    public int CommentCount { get; set; }

    [JsonProperty(PropertyName = "edge_media_to_caption.edges[0].node.text")]
    public string Caption { get; set; }

    [JsonProperty(PropertyName = "display_url")]
    public string Image { get; set; }

    [JsonProperty(PropertyName = "dimensions.width")]
    public int Width { get; set; }

    [JsonProperty(PropertyName = "dimensions.height")]
    public int Height { get; set; }

    [JsonProperty(PropertyName = "shortcode")]
    public string Shortcode { get; set; }
}

JsonPathConverter.cs JsonPathConverter.cs

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

        foreach (PropertyInfo prop in objectType.GetProperties().Where(p => p.CanRead && p.CanWrite))
        {
            JsonPropertyAttribute att = prop.GetCustomAttributes(true)
                                            .OfType<JsonPropertyAttribute>()
                                            .FirstOrDefault();

            string jsonPath = (att != null ? att.PropertyName : prop.Name);
            JToken token = jo.SelectToken(jsonPath);

            if (token != null && token.Type != JTokenType.Null)
            {
                object value = token.ToObject(prop.PropertyType, serializer);
                prop.SetValue(targetObj, value, null);
            }
        }

        return targetObj;
    }

    public override bool CanConvert(Type objectType)
    {
        return false;
    }

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

The problem is when I want to send that object as a JSON: It returns fields with PropertyName value, instead of attribute name:问题是当我想将 object 作为 JSON 发送时:它返回具有 PropertyName 值的字段,而不是属性名称:

[
  {
    "id": "2322829435103312153",
    "taken_at_timestamp": 1591122868,
    "edge_media_preview_like.count": 24,
    "edge_media_to_comment.count": 0,
    "edge_media_to_caption.edges[0].node.text": "...",
    "display_url": "...",
    "dimensions.width": 1080,
    "dimensions.height": 1080,
    "shortcode": "..."
  }
  ...
]

Is there a way to use PropertyName for deserialize but attribute name for serialize?有没有办法使用 PropertyName 进行反序列化,但使用属性名称进行序列化?

UPDATE更新

As requested, this is how I use deserialization and serialization:根据要求,这就是我使用反序列化和序列化的方式:

Deserialize反序列化

JArray response = "[{ ... }]";

List<Post> posts = new List<Post>();

foreach (JObject node in response)
{
    Post post = JsonConvert.DeserializeObject<Post>(node.ToString());
    posts.Add(post);
}

Serialize连载

I just return the List inside an "Ok" IActionResult :我只是在“确定”的IActionResult中返回列表

Ok(posts);

You can use ContractResolver some like below.您可以像下面这样使用ContractResolver

var jsondata = JsonConvert.SerializeObject(data, new JsonSerializerSettings { ContractResolver = new ShouldUseMemberNameContractResolver() });


public class ShouldUseMemberNameContractResolver : DefaultContractResolver
{
    public new static readonly ShouldUseMemberNameContractResolver Instance = new ShouldUseMemberNameContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Post))
        {
            property.PropertyName = member.Name;
        }

        return property;
    }
}

I solved it through WriteJson method as Chetan Ranpariya suggested:我按照Chetan Ranpariya 的建议通过 WriteJson 方法解决了它:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    JArray result = new JArray();

    if (value.GetType().IsGenericType && value is IEnumerable)
    {
        IEnumerable list = value as IEnumerable;

        foreach(var obj in list)
        {
            result.Add(GetObjectJson(obj));
        }
    }

    result.WriteTo(writer);
}

private JObject GetObjectJson(object obj)
{
    JObject jObj = new JObject();
    PropertyInfo[] props = obj.GetType().GetProperties();

    foreach (PropertyInfo prop in props)
    {
        if (!prop.PropertyType.ToString().Contains("System"))
            jObj.Add(char.ToLowerInvariant(prop.Name[0]) + prop.Name.Substring(1), GetObjectJson(prop.GetValue(obj)));
        else
            jObj.Add(char.ToLowerInvariant(prop.Name[0]) + prop.Name.Substring(1), JToken.FromObject(prop.GetValue(obj)));
    }

    return jObj;
}

As in object value parameter I receive the object (which is a list, in this case) I cast it and iterate over it's objects.object value参数一样,我收到 object(在本例中是一个列表)我对其进行转换并遍历它的对象。

Since every object can have primitive and non primitive properties, I created a separated function to use it recursively in case I found a nested object.由于每个 object 都可以具有原始和非原始属性,因此我创建了一个单独的 function 以递归使用它,以防我发现嵌套的 object。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM