将JSON对象反序列化为嵌套的C#对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将JSON对象反序列化为嵌套的C#对象相关的知识,希望对你有一定的参考价值。

编辑:我想我应该提到我无法控制JSON,我知道通常我的C#对象应该与JSON匹配。我的问题不是“为什么不进行反序列化?”。我知道为什么不是。我问是否有办法按照我要求的方式反序列化JSON。

我正在使用Newtonsoft.Json。

我有一个包含1个对象的JSON字符串。我需要使用嵌套对象将该对象反序列化为C#对象。

所以我要说我的JSON看起来像这样。

{
    "id": 123,
    "userName": "fflintstone",
    "address": "345 Cave Stone Road",
    "address2": "",
    "city": "Bedrock",
    "state": "AZ",
    "zip": "",   
}

这是我的C#对象

public class Customer
{
    public long Id { get; set; }

    public string UserName { get; set; }

    public AddressModel Address { get; set; }
}

AddressModel Address属性是嵌套对象。该对象包含实际的地址属性。所以我需要反序列化我的JSON对象,以便将id和userName添加到Customer对象,然后将地址字段添加到嵌套的Address对象。

我无法找到一种内置于newtonsoft中的方法来实现这一目标。有任何想法吗?

答案

您可以使用自定义JsonConverter执行此操作。

public class CustomerJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is Customer customer)
        {
            var token = new JObject
            {
                ["id"] = customer.Id,
                ["userName"] = customer.UserName,
                ["address"] = customer.Address.Address,
                ["address2"] = customer.Address.Address2,
                ["city"] = customer.Address.City,
                ["state"] = customer.Address.State,
                ["zip"] = customer.Address.ZIP
            };

            token.WriteTo(writer);
        }
        else
        {
            throw new InvalidOperationException();
        }

    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JToken.ReadFrom(reader);

        if (obj.Type != JTokenType.Object)
        {
            return null;
        }

        return new Customer
        {
            Id = (long) obj["id"],
            UserName = (string) obj["userName"],
            Address = obj.ToObject<AddressModel>()
        };
    }

    public override bool CanRead => true;

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Customer);
    }
}

JsonConverterAttribute类的Customer一起。

[JsonConverter(typeof(CustomerJsonConverter))]
public class Customer
{
    public long Id { get; set; }

    public string UserName { get; set; }

    public AddressModel Address { get; set; }
}

public class AddressModel
{
    public string Address { get; set; }

    public string Address2 { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string ZIP { get; set; }
}

并使用如下:

var customer = JsonConvert.DeserializeObject<Customer>(customerJson);

或者,您可以简单地使用中间映射模型。

public class CustomerFlattened
{
    public long Id { get; set; }

    public string UserName { get; set; }

    public string Address { get; set; }

    public string Address2 { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string ZIP { get; set; }

    public Customer ToCustomer()
    {
        return new Customer
        {
            Id = Id,
            UserName = UserName,
            Address = new AddressModel
            {
                Address = Address,
                Address2 = Address2,
                City = City,
                State = State,
                ZIP = ZIP
            }
        };
    }

    public static CustomerFlattened FromCustomer(Customer customer)
    {
        return new CustomerFlattened
        {
            Id = customer.Id,
            UserName = customer.UserName,
            Address = customer.Address.Address,
            Address2 = customer.Address.Address2,
            City = customer.Address.City,
            State = customer.Address.State,
            ZIP = customer.Address.ZIP
        };
    }
}

并使用如下:

var customer =
    JsonConvert.Deserialize<CustomerFlattened>(
        jsonOriginal
    )
    .ToCustomer();

var customerFlattened = CustomerFlattened.FromCustomer(customer);

var jsonConverted = JsonConvert.Serialize(customerFlattened );
另一答案

如果您是设置JSON的人,那么您的代码将使用JSON,那么您的JSON就不会与您的对象相匹配。

它应该如下所示:

{
    "id": 123,
    "userName": "fflintstone",
    "address": {
        // address properties here
    }
} 

否则,您需要更新C#对象以匹配JSON,这意味着JSON中每个项目的各个属性:

public class Customer
{
    public long Id { get; set; }

    public string UserName { get; set; }

    public string Address { get; set; }

    public string Address2 { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string ZIP { get; set; }
}

此外,如果您正在设置这两个,街道名称可能不应命名为“地址”。对我来说,“地址”这个词意味着街道名称,数量,城市,州和ZIP作为一个整体。

如果您无法控制JSON,那么就没有真正的方法可以使用类似Json.Net的东西将JSON干净地反序列化到您的对象中。您需要设置某种映射器,或者直接查找属性以获取它们的值以将它们添加到对象中。您可以将JSON解析为JObject,然后访问所需的属性。

JObject foo = JObject.Parse(//your JSON string here);
customer.Address = (string)foo["Address"];
customer.Address2 = (string)foo["Address2];

通常,如果您不是控制JSON结构的人,那么最好只让您的对象与您给出的JSON相匹配。

另一答案

如果您愿意使用其他工具,则可以创建与JSON匹配的DTO,然后使用Automapper等映射器将DTO reverse flatten放入对象模型中。

另一答案

由于您的JSON和对象模型不匹配,因此您必须反序列化为临时对象并自行映射字段。在这个例子中,我反序列化为一个像模板一样使用的匿名对象。

//This is our test data
var input = @"{
    ""id"": 123,
    ""userName"": ""fflintstone"",
    ""address"": ""345 Cave Stone Road"",
    ""address2"": """",
    ""city"": ""Bedrock"",
    ""state"": ""AZ"",
    ""zip"": """"   
}";

//This anonymous type will be used as a template to deserialize the test data
var template = new 
{ 
    id = default(int),
    userName = default(string),
    address = default(string),
    address2 = default(string),
    city = default(string),
    state = default(string),
    zip = default(string)
};

//Deserialize
var temp = JsonConvert.DeserializeAnonymousType(input, template);

//Use the deserialized object to create an AddressModel
var address = new AddressModel
{
    Address = temp.address,
    Address2 = temp.address,
    City = temp.city,
    State = temp.state,
    Zip = temp.zip
};

//Create a customer using the deserialized data and our new AddressModel instance
var customer = new Customer
{
    Id = temp.id,
    UserName = temp.userName,
    Address = address
};

//Output a couple fields to check
Console.WriteLine("{0} {1}", customer.Id, customer.Address.City);

输出:

123 Bedrock

Working example on DotNetFiddle

另一答案

首先,对于要使用AdressModel类创建的对象,您需要更改JSON文档,我已更改为:

  {   "id": 123,   "userName": "fflintstone",   "Address": {
"address": "345 Cave Stone Road",
"address2": "",
"city": "Bedrock",
"state": "AZ",
"zip": ""   } }

然后对于模型我创建了这两个模型:

public class Customer
{
    public long id { get; set; }
    public string userName { get; set; }
    public AddressModel Address { get; set; }
}

public class AddressModel
{
    public string address { get; set; }
    public string address2 { get; set; }
    public string city { get; set; }
    public string state { get; set; }
    public string zip { get; set; }
}

现在要反序列化Json文档,你可以这样做:

Customer jsonConverted = new Customer();

        using (StreamReader r = new StreamReader(HostingEnvironment.ApplicationPhysicalPath + @"infoFile.json"))
        {
            var json = r.ReadToEnd();
            jsonConverted = JsonConvert.DeserializeObject<Customer>(json);
        }

(我正在测试一个MVC项目)enter image description here

以上是关于将JSON对象反序列化为嵌套的C#对象的主要内容,如果未能解决你的问题,请参考以下文章

Json.NET:将嵌套数组反序列化为强类型对象

将嵌套的 JSON 反序列化为 C# 对象

System.Text.Json - 将嵌套对象反序列化为字符串

将嵌套的 JSON 字符串反序列化为 Java 对象

无法将当前 JSON 数组(例如 [1,2,3])反序列化为具有复杂和嵌套对象的类型

Json.NET:反序列化嵌套字典