JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.64k stars 3.24k forks source link

Deserializing JSON with JsonProperty("$type") not working #2934

Open x3igh7 opened 5 months ago

x3igh7 commented 5 months ago

Source/destination types

    public class GetPostThreadResponse
    {
        public ThreadViewPost Thread { get; set; }
    }

public class ThreadViewPost : InvalidThreadPost
{
    public PostView Post { get; set; }
    public ThreadViewPost Parent { get; set; }
}

public class PostView {
    public string Uri { get; set; }
    public string Cid { get; set; }
    public Author Author { get; set; }
    public EmbedView? Embed { get; set; }
    public Post Record { get; set; }
    public int ReplyCount { get; set; }
    public int RepostCount { get; set; }
    public int LikeCount { get; set; }
}

public class Post {
    [JsonProperty(PropertyName = "$type")]
    public string Type { get; set; }
    public string Text { get; set; }
    public DateTime CreatedAt { get; set; }
    public Embed? Embed { get; set; }
    public IEnumerable<Facet>? Facets { get; set; }
}

Source/destination JSON

{
    "thread": {
        "$type": "app.bsky.feed.defs#threadViewPost",
        "post": {
            "uri": "at://did:plc:flstla3vf27s7vpb4xeipci5/app.bsky.feed.post/3kkucvekaoe2e",
            "cid": "bafyreicquaqsvkq4c4k6nkcahdcqurzl5qz55tv7jhgbd37v5atiaokyju",
            "author": {
                "did": "did:plc:flstla3vf27s7vpb4xeipci5",
                "handle": "wes.readonlymemo.com",
                "displayName": "Wes Fenlon",
                "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:flstla3vf27s7vpb4xeipci5/bafkreidlhdi2g7aixhv7xku3dkghc6utbaiesafbagmwpjj44sz4riuwza@jpeg",
                "viewer": {
                    "muted": false,
                    "blockedBy": false
                },
                "labels": []
            },
            "record": {
                "$type": "app.bsky.feed.post",
                "createdAt": "2024-02-07T22:28:26.899Z",
                "embed": {
                    "$type": "app.bsky.embed.images",
                    "images": [{
                            "alt": "",
                            "aspectRatio": {
                                "height": 1125,
                                "width": 2000
                            },
                            "image": {
                                "$type": "blob",
                                "ref": {
                                    "$link": "bafkreicigdqt54t3z6hnazfuu6bzh35nrrmmqd56lic5t7zdbjz3z7ul34"
                                },
                                "mimeType": "image/jpeg",
                                "size": 590427
                            }
                        }
                    ]
                },
                "facets": [{
                        "features": [{
                                "$type": "app.bsky.richtext.facet#link",
                                "uri": "https://www.pcgamer.com/the-impact-of-16000-games-industry-layoffs-in-one-chart/"
                            }
                        ],
                        "index": {
                            "byteEnd": 299,
                            "byteStart": 268
                        }
                    }
                ],
                "langs": ["en"],
                "text": "At PC Gamer we've been talking about how to convey the scale of 16,000+ game devs being laid off in a year, without forgetting that number represents individual people with their own stories. This is our first attempt: 16,000 dots, alongside voices of those affected. www.pcgamer.com/the-impact-o..."
            },
            "replyCount": 37,
            "repostCount": 774,
            "likeCount": 1474,
            "indexedAt": "2024-02-07T22:28:26.899Z",
            "viewer": {
                "like": "at://did:plc:wq4toysg4gy5dwrhi55ct4xp/app.bsky.feed.like/3kkvw2lkyui2b"
            },
            "labels": []
        },
    }
}

Expected behavior

Expected the Type property value with [JsonProperty(PropertyName = "$type")] be properly set.

Actual behavior

Type property is null.

Steps to reproduce

var result =
    await this.HttpClient.GetAsync($"app.bsky.feed.getPostThread?uri={Uri.EscapeDataString(postUri)}");

var content = await result.Content.ReadAsStringAsync();

var getPostThreadResponse =
    JsonConvert.DeserializeObject<GetPostThreadResponse>(content);

I swear this was working properly at one point, but it's simply failing now. I have a few other properties that I've mapped with JsonProperty in the project and those do work correctly, although they do not have $ in the name. I'm on .net6 still.

elgonzo commented 5 months ago

Newtonsoft.Json treats $type and some other json property names starting with "$" as metadata keywords by default and not as ordinary property names, unless you deactivate the handling of metadata properties by configuring the MetadataPropertyHandling setting as MetadataPropertyHandling.Ignore.

(The name "Ignore" for this setting is a bit misleading, as property names like $type are not ignored. Rather, the handling of metadata properties is being disabled, turning metadata keywords like $type into ordinary json property names.)

I swear this was working properly at one point

I guess in the past you did set MetadataPropertyHandling.Ignore, but forgot to set it now...

x3igh7 commented 5 months ago

Newtonsoft.Json treats $type and some other json property names starting with "$" as metadata keywords by default and not as ordinary property names, unless you deactivate the handling of metadata properties by configuring the MetadataPropertyHandling setting as MetadataPropertyHandling.Ignore.

(The name "Ignore" for this setting is a bit misleading, as property names like $type are not ignored. Rather, the handling of metadata properties is being disabled, turning metadata keywords like $type into ordinary json property names.)

I swear this was working properly at one point

I guess in the past you did set MetadataPropertyHandling.Ignore, but forgot to set it now...

Thanks for the suggestion - I will give it a try.