2

我正在尝试使用 ServiceStack.Text 反序列化 twitter RateLimit Json。我为 JSON 下拉创建了一个适当的 DTO 对象(查看文章底部)。我使用 System.Runtime.Serialization 将字段重命名为更友好的名称(我在 Twitter 的许多其他 DTO 对象中都使用了它)。反序列化对象时,RateLimitContext 会正确反序列化,但是 Resources 下的所有项目都会反序列化为 null。我怀疑这个特定对象的 DataMemberNames 有问题,因为它们包含几个“特殊”字符。这是 ServiceStack.Text 中的错误还是我的 DTO 中缺少某些内容?

[DataContract]
public class RateLimitResult
{
    [DataMember(Name = "rate_limit_context")]
    public RateLimitContextEntry RateLimitContext { get; set; }

    [DataContract]
    public class RateLimitContextEntry
    {
        [DataMember(Name = "access_token")]
        public string AccessToken { get; set; }
    }

    [DataMember(Name = "resources")]
    public ResourcesEntry Resources { get; set; }

    [DataContract]
    public class RateLimitEntry
    {
        [DataMember(Name = "remaining")]
        public int Remaining { get; set; }

        [DataMember(Name = "reset")]
        public long Reset { get; set; }

        [DataMember(Name = "limit")]
        public int Limit { get; set; }
    }

    [DataContract]
    public class HelpEntry
    {
        [DataMember(Name = "/help/privacy")]
        public RateLimitEntry Privacy { get; set; }

        [DataMember(Name = "/help/configuration")]
        public RateLimitEntry Configuration { get; set; }

        [DataMember(Name = "/help/tos")]
        public RateLimitEntry Tos { get; set; }

        [DataMember(Name = "/help/languages")]
        public RateLimitEntry Languages { get; set; }
    }

    [DataContract]
    public class StatusesEntry
    {
        [DataMember(Name = "/statuses/oembed")]
        public RateLimitEntry Oembed { get; set; }

        [DataMember(Name = "/statuses/user_timeline")]
        public RateLimitEntry UserTimeline { get; set; }

        [DataMember(Name = "/statuses/mentions_timeline")]
        public RateLimitEntry MentionsTimeline { get; set; }

        [DataMember(Name = "/statuses/home_timeline")]
        public RateLimitEntry HomeTimeline { get; set; }

        [DataMember(Name = "/statuses/show/:id")]
        public RateLimitEntry ShowId { get; set; }

        [DataMember(Name = "/statuses/retweets/:id")]
        public RateLimitEntry RetweetsId { get; set; }

        [DataMember(Name = "/statuses/retweets_of_me")]
        public RateLimitEntry RetweetsOfMe { get; set; }
    }

    [DataContract]
    public class UsersEntry
    {
        [DataMember(Name = "/users/search")]
        public RateLimitEntry Search { get; set; }

        [DataMember(Name = "/users/suggestions")]
        public RateLimitEntry Suggestions { get; set; }

        [DataMember(Name = "/users/contributors")]
        public RateLimitEntry Contributors { get; set; }

        [DataMember(Name = "/users/suggestions/:slug/members")]
        public RateLimitEntry SuggestionsSlugMembers { get; set; }

        [DataMember(Name = "/users/suggestions/:slug")]
        public RateLimitEntry SuggestionsSlug { get; set; }

        [DataMember(Name = "/users/contributees")]
        public RateLimitEntry Contributees { get; set; }

        [DataMember(Name = "/users/lookup")]
        public RateLimitEntry Lookup { get; set; }

        [DataMember(Name = "/users/profile_banner")]
        public RateLimitEntry ProfileBanner { get; set; }
    }

    [DataContract]
    public class SearchEntry
    {
        [DataMember(Name = "/search/tweets")]
        public RateLimitEntry Tweets { get; set; }
    }

    [DataContract]
    public class TrendsEntry
    {
        [DataMember(Name = "/trends/available")]
        public RateLimitEntry Available { get; set; }

        [DataMember(Name = "/trends/place")]
        public RateLimitEntry Place { get; set; }

        [DataMember(Name = "/trends/closest")]
        public RateLimitEntry Closest { get; set; }
    }

    [DataContract]
    public class SavedSearchesEntry
    {
        [DataMember(Name = "/saved_searches/destroy/:id")]
        public RateLimitEntry DestroyId { get; set; }

        [DataMember(Name = "/saved_searches/list")]
        public RateLimitEntry List { get; set; }

        [DataMember(Name = "/saved_searches/show/:id")]
        public RateLimitEntry ShowId { get; set; }
    }

    [DataContract]
    public class FavoritesEntry
    {
        [DataMember(Name = "/favorites/list")]
        public RateLimitEntry List { get; set; }
    }

    [DataContract]
    public class AccountEntry
    {
        [DataMember(Name = "/account/verify_credentials")]
        public RateLimitEntry VerifyCredentials { get; set; }

        [DataMember(Name = "/account/settings")]
        public RateLimitEntry Settings { get; set; }
    }

    [DataContract]
    public class DirectMessagesEntry
    {
        [DataMember(Name = "/direct_messages/show")]
        public RateLimitEntry Show { get; set; }

        [DataMember(Name = "/direct_messages/sent_and_received")]
        public RateLimitEntry SentAndReceived { get; set; }

        [DataMember(Name = "/direct_messages/sent")]
        public RateLimitEntry Sent { get; set; }

        [DataMember(Name = "/direct_messages")]
        public RateLimitEntry Messages { get; set; }
    }

    [DataContract]
    public class FriendsEntry
    {
        [DataMember(Name = "/friends/ids")]
        public RateLimitEntry Ids { get; set; }

        [DataMember(Name = "/friends/list")]
        public RateLimitEntry List { get; set; }
    }

    [DataContract]
    public class FollowersEntry
    {
        [DataMember(Name = "/followers/list")]
        public RateLimitEntry List { get; set; }

        [DataMember(Name = "/followers/ids")]
        public RateLimitEntry Ids { get; set; }
    }

    [DataContract]
    public class PromptsEntry
    {
        [DataMember(Name = "/prompts/record_event")]
        public RateLimitEntry RecordEvent { get; set; }

        [DataMember(Name = "/prompts/suggest")]
        public RateLimitEntry Suggest { get; set; }
    }

    [DataContract]
    public class GeoEntry
    {
        [DataMember(Name = "/geo/similar_places")]
        public RateLimitEntry SimilarPlaces { get; set; }

        [DataMember(Name = "/geo/search")]
        public RateLimitEntry Search { get; set; }

        [DataMember(Name = "/geo/reverse_geocode")]
        public RateLimitEntry ReverseGeocode { get; set; }

        [DataMember(Name = "/geo/id/:place_id")]
        public RateLimitEntry PlaceId { get; set; }
    }

    [DataContract]
    public class BlocksEntry
    {
        [DataMember(Name = "/blocks/ids")]
        public RateLimitEntry Ids { get; set; }

        [DataMember(Name = "/blocks/list")]
        public RateLimitEntry List { get; set; }
    }

    [DataContract]
    public class FriendshipsEntry
    {
        [DataMember(Name = "/friendships/incoming")]
        public RateLimitEntry Incoming { get; set; }

        [DataMember(Name = "/friendships/lookup")]
        public RateLimitEntry Lookup { get; set; }

        [DataMember(Name = "/friendships/outgoing")]
        public RateLimitEntry Outgoing { get; set; }

        [DataMember(Name = "/friendships/no_retweets/ids")]
        public RateLimitEntry NoRetweetsIds { get; set; }

        [DataMember(Name = "/friendships/show")]
        public RateLimitEntry Show { get; set; }
    }

    [DataContract]
    public class ApplicationEntry
    {
        [DataMember(Name = "/application/rate_limit_status")]
        public RateLimitEntry RateLimitStatus { get; set; }
    }

    [DataContract]
    public class ListsEntry
    {
        [DataMember(Name = "/lists/subscribers")]
        public RateLimitEntry Subscribers { get; set; }

        [DataMember(Name = "/lists/list")]
        public RateLimitEntry List { get; set; }

        [DataMember(Name = "/lists/memberships")]
        public RateLimitEntry Memberships { get; set; }

        [DataMember(Name = "/lists/ownerships")]
        public RateLimitEntry Ownerships { get; set; }

        [DataMember(Name = "/lists/subscriptions")]
        public RateLimitEntry Subscriptions { get; set; }

        [DataMember(Name = "/lists/members")]
        public RateLimitEntry Members { get; set; }

        [DataMember(Name = "/lists/subscribers/show")]
        public RateLimitEntry ShowSubscribers { get; set; }

        [DataMember(Name = "/lists/statuses")]
        public RateLimitEntry Statuses { get; set; }

        [DataMember(Name = "/lists/members/show")]
        public RateLimitEntry ShowMembers { get; set; }

        [DataMember(Name = "/lists/show")]
        public RateLimitEntry Show { get; set; }
    }

    [DataContract]
    public class ResourcesEntry
    {
        [DataMember(Name = "lists")]
        public ListsEntry Lists { get; set; }

        [DataMember(Name = "application")]
        public ApplicationEntry Application { get; set; }

        [DataMember(Name = "friendships")]
        public FriendshipsEntry Friendships { get; set; }

        [DataMember(Name = "blocks")]
        public BlocksEntry Blocks { get; set; }

        [DataMember(Name = "geo")]
        public GeoEntry Geo { get; set; }

        [DataMember(Name = "users")]
        public UsersEntry Users { get; set; }

        [DataMember(Name = "prompts")]
        public PromptsEntry Prompts { get; set; }

        [DataMember(Name = "followers")]
        public FollowersEntry Followers { get; set; }

        [DataMember(Name = "statuses")]
        public StatusesEntry Statuses { get; set; }

        [DataMember(Name = "help")]
        public HelpEntry Help { get; set; }

        [DataMember(Name = "friends")]
        public FriendsEntry Friends { get; set; }

        [DataMember(Name = "direct_messages")]
        public DirectMessagesEntry DirectMessages { get; set; }

        [DataMember(Name = "account")]
        public AccountEntry Account { get; set; }

        [DataMember(Name = "favorites")]
        public FavoritesEntry Favorites { get; set; }

        [DataMember(Name = "saved_searches")]
        public SavedSearchesEntry SavedSearches { get; set; }

        [DataMember(Name = "search")]
        public SearchEntry Search { get; set; }

        [DataMember(Name = "trends")]
        public TrendsEntry Trends { get; set; }
    }
}

以下是 API 返回的示例 Json:

{
"rate_limit_context": {
    "access_token": "ajksldjalsda-akjflkasjfltpowfaalksjdfAPQPIJFaflkjaq"
},
"resources": {
    "lists": {
        "/lists/subscribers": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/lists/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/memberships": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/ownerships": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/subscriptions": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/members": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/lists/subscribers/show": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/statuses": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/lists/members/show": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/lists/show": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "application": {
        "/application/rate_limit_status": {
            "limit": 180,
            "remaining": 178,
            "reset": 1380314022
        }
    },
    "friendships": {
        "/friendships/incoming": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/friendships/lookup": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/friendships/outgoing": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/friendships/no_retweets/ids": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/friendships/show": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        }
    },
    "blocks": {
        "/blocks/ids": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/blocks/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "geo": {
        "/geo/similar_places": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/geo/search": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/geo/reverse_geocode": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/geo/id/:place_id": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "users": {
        "/users/profile_banner": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/users/suggestions/:slug/members": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/users/show/:id": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/users/suggestions": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/users/lookup": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/users/search": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/users/contributors": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/users/contributees": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/users/suggestions/:slug": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "prompts": {
        "/prompts/record_event": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/prompts/suggest": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "followers": {
        "/followers/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/followers/ids": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "statuses": {
        "/statuses/mentions_timeline": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/statuses/show/:id": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/statuses/oembed": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/statuses/retweeters/ids": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/statuses/home_timeline": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/statuses/user_timeline": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        },
        "/statuses/retweets/:id": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/statuses/retweets_of_me": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "help": {
        "/help/privacy": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/help/tos": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/help/configuration": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/help/languages": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "friends": {
        "/friends/ids": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/friends/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "direct_messages": {
        "/direct_messages/show": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/direct_messages/sent_and_received": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/direct_messages/sent": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/direct_messages": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "account": {
        "/account/verify_credentials": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/account/settings": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "favorites": {
        "/favorites/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "saved_searches": {
        "/saved_searches/destroy/:id": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/saved_searches/list": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/saved_searches/show/:id": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    },
    "search": {
        "/search/tweets": {
            "limit": 180,
            "remaining": 180,
            "reset": 1380314146
        }
    },
    "trends": {
        "/trends/available": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/trends/place": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        },
        "/trends/closest": {
            "limit": 15,
            "remaining": 15,
            "reset": 1380314146
        }
    }
}

}

4

1 回答 1

0

[DataMember]名称仅用于指定单个字段名称别名,即它们不是嵌套的,并且不能像您在此处所做的那样包含非字母数字字符。

这是用于 ServiceStack序列化程序的 Twitter DTO 示例。

于 2013-09-30T15:23:19.253 回答