Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize JSON object into nested C# object

Edit: I guess I should mention that I do not have control over the JSON and I know that typically my C# object should match the JSON. My question wasn't "why isn't this deserializing?". I know why it's not. I am asking if there is a way to deserialize the JSON the way that I am asking.

I am using Newtonsoft.Json.

I have a JSON string containing 1 object. I need to deserialize that object into a C# object with a nested object.

So lets say my JSON looks like this.

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

And here is my C# object

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

    public string UserName { get; set; }

    public AddressModel Address { get; set; }
}

The AddressModel Address property is a nested object. That object contains the actual address properties. So I need to deserialze my JSON object so that id and userName are added to the Customer object and then the address fields are added to the nested Address object.

I haven't been able to find a way that is built into newtonsoft to accomplish this. Any ideas?

like image 615
user1289451 Avatar asked Jan 02 '23 05:01

user1289451


1 Answers

You can do this using a custom 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);
    }
}

Along with a JsonConverterAttribute on the Customer class.

[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; }
}

And utilize as such:

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

alternatively, you could simply have an intermediate mapping model.

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
        };
    }
}

And utilize as such:

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

var customerFlattened = CustomerFlattened.FromCustomer(customer);

var jsonConverted = JsonConvert.Serialize(customerFlattened );
like image 83
cwharris Avatar answered Jan 04 '23 23:01

cwharris