使用 JsonConvert.derializeObject 将 Json 反序列化为 C # POCO 类

下面是我的简单 User POCO 类:

/// <summary>
/// The User class represents a Coderwall User.
/// </summary>
public class User
{
/// <summary>
/// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
/// </summary>
public string Username { get; set; }


/// <summary>
/// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
/// </summary>
public string Name { get; set; }


/// <summary>
/// A User's location. eh: "Bolivia, USA, France, Italy"
/// </summary>
public string Location { get; set; }


public int Endorsements { get; set; } //Todo.
public string Team { get; set; } //Todo.


/// <summary>
/// A collection of the User's linked accounts.
/// </summary>
public List<Account> Accounts { get; set; }


/// <summary>
/// A collection of the User's awarded badges.
/// </summary>
public List<Badge> Badges { get; set; }


}

我用来将 JSON 响应反序列化为 User对象(实际的 JSON 来电)的方法是:

private User LoadUserFromJson(string response)
{
var outObject = JsonConvert.DeserializeObject<User>(response);
return outObject;
}

这引发了一个例外:

无法反序列化当前 JSON 对象(例如{“ name”: “ value”}) 打字 ‘ System. Collections. Generic. List‘1[ CoderwallDotNet. Api. Models.Account ] 因为该类型需要一个 JSON 数组(例如[1,2,3])来反序列化 正确。

要修复此错误,可以将 JSON 更改为 JSON 数组 (例如[1,2,3])或更改反序列化类型,使其成为正常的 NET 类型(例如,不是类似于整数的基元类型,也不是集合) 类似于数组或列表) ,可以从 JSON 反序列化 也可以将 JsonObjectAttribute 添加到类型中以强制它 从 JSON 对象反序列化, 位置129。

我以前从来没有使用过这个 SerializeObject 方法,所以现在有点困在这里了。

我已经确保 POCO 类中的属性名称与 JSON 响应中的名称相同。

我可以尝试将 JSON 反序列化到这个 POCO 类中吗?

410468 次浏览

The accounts property is defined like this:

"accounts":{"github":"sergiotapia"}

Your POCO states this:

public List<Account> Accounts { get; set; }

Try using this Json:

"accounts":[{"github":"sergiotapia"}]

An array of items (which is going to be mapped to the list) is always enclosed in square brackets.

Edit: The Account Poco will be something like this:

class Account {
public string github { get; set; }
}

and maybe other properties.

Edit 2: To not have an array use the property as follows:

public Account Accounts { get; set; }

with something like the sample class I've posted in the first edit.

to fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the
deserialized type so that it is a normal .NET type (e.g. not a primitive type like
integer, not a collection type like an array or List) that can be deserialized from a
JSON object.`

The whole message indicates that it is possible to serialize to a List object, but the input must be a JSON list. This means that your JSON must contain

"accounts" : [{<AccountObjectData}, {<AccountObjectData>}...],

Where AccountObject data is JSON representing your Account object or your Badge object

What it seems to be getting currently is

"accounts":{"github":"sergiotapia"}

Where accounts is a JSON object (denoted by curly braces), not an array of JSON objects (arrays are denoted by brackets), which is what you want. Try

"accounts" : [{"github":"sergiotapia"}]

You could create a JsonConverter. See here for an example thats similar to your question.

Here is a working example.

Keypoints are:

  • Declaration of Accounts
  • Use of JsonProperty attribute

.

using (WebClient wc = new WebClient())
{
var json = wc.DownloadString("http://coderwall.com/mdeiters.json");
var user = JsonConvert.DeserializeObject<User>(json);
}

-

public class User
{
/// <summary>
/// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
/// </summary>
[JsonProperty("username")]
public string Username { get; set; }


/// <summary>
/// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }


/// <summary>
/// A User's location. eh: "Bolivia, USA, France, Italy"
/// </summary>
[JsonProperty("location")]
public string Location { get; set; }


[JsonProperty("endorsements")]
public int Endorsements { get; set; } //Todo.


[JsonProperty("team")]
public string Team { get; set; } //Todo.


/// <summary>
/// A collection of the User's linked accounts.
/// </summary>
[JsonProperty("accounts")]
public Account Accounts { get; set; }


/// <summary>
/// A collection of the User's awarded badges.
/// </summary>
[JsonProperty("badges")]
public List<Badge> Badges { get; set; }
}


public class Account
{
public string github;
}


public class Badge
{
[JsonProperty("name")]
public string Name;
[JsonProperty("description")]
public string Description;
[JsonProperty("created")]
public string Created;
[JsonProperty("badge")]
public string BadgeUrl;
}

Another, and more streamlined, approach to deserializing a camel-cased JSON string to a pascal-cased POCO object is to use the CamelCasePropertyNamesContractResolver.

It's part of the Newtonsoft.Json.Serialization namespace. This approach assumes that the only difference between the JSON object and the POCO lies in the casing of the property names. If the property names are spelled differently, then you'll need to resort to using JsonProperty attributes to map property names.

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;


. . .


private User LoadUserFromJson(string response)
{
JsonSerializerSettings serSettings = new JsonSerializerSettings();
serSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
User outObject = JsonConvert.DeserializeObject<User>(jsonValue, serSettings);


return outObject;
}

That's not exactly what I had in mind. What do you do if you have a generic type to only be known at runtime?

public MyDTO toObject() {
try {
var methodInfo = MethodBase.GetCurrentMethod();
if (methodInfo.DeclaringType != null) {
var fullName = methodInfo.DeclaringType.FullName + "." + this.dtoName;
Type type = Type.GetType(fullName);
if (type != null) {
var obj = JsonConvert.DeserializeObject(payload);
//var obj = JsonConvert.DeserializeObject<type.MemberType.GetType()>(payload);  // <--- type ?????
...
}
}


// Example for java..   Convert this to C#
return JSONUtil.fromJSON(payload, Class.forName(dtoName, false, getClass().getClassLoader()));
} catch (Exception ex) {
throw new ReflectInsightException(MethodBase.GetCurrentMethod().Name, ex);
}
}

Along the lines of the accepted answer, if you have a JSON text sample you can plug it in to this converter, select your options and generate the C# code.

If you don't know the type at runtime, this topic looks like it would fit.

dynamically deserialize json into any object passed in. c#

May be late but using QuickType is the easiest way to do that:

https://app.quicktype.io/

For anyone having this problem i was not seeing the json value properly. https://jsonutils.com/ there you can check the classes that should be generated and return ONLY ONE of those classes once you read the json in your code.

For example i needed a booklist object so my code should only read one

res = await response.Content.ReadAsAsync<BookList>();

Where booklist looks something like

    public class BookList
{


[JsonProperty("data")]
public IList<Datum> Data { get; set; }
}

And in that list have smaller book clasess that the converter named Datum (just books)

    public class Datum
{


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


[JsonProperty("isbn")]
public string Isbn { get; set; }
}

Again, if you have doubts https://jsonutils.com/