0

I have this incoming request with this payload

{
    count : 10,
    supplier : {
        id : 342,
        name : 'test'
    },
    title : 'some title'
}

and I have this model in my c# code

class SomeModel 
{
    public string Title { get; set; } 
    public int SupplierId { get; set; }
    public double Amount { get; set; }
}

This is my controller method

public IActionResult Foo(SomeModel data) 
{
    //...
}

I would like to map the property count in request payload to Amount property in my c# model and mapping the value of supplier.id into SupplierId.

I'm using Newtonsoft Json.NET library for binding

7
  • 4
    I would recommend you to simply edit your model to match the actual payload. Commented Jan 22, 2020 at 17:53
  • that's what I was going to do, but I wondered if there was some way to do the mapping Commented Jan 22, 2020 at 17:57
  • 1
    You could write custom binders but it’s really not worth the effort. Commented Jan 22, 2020 at 18:00
  • You could try using, learn.microsoft.com/en-us/aspnet/core/mvc/advanced/… But this might be complex. I would recommend creating optional parameters something like public type OptionalPropName {get; set;} = null; Commented Jan 22, 2020 at 19:06
  • A simpler approach would be to use JsonProperty data annotation newtonsoft.com/json/help/html/JsonPropertyName.htm Commented Jan 22, 2020 at 19:21

1 Answer 1

2

Obviously the simplest way is to create a class corresponding to payload stucture like this

public class SomeModel
{
    public string Title { get; set; }

    public double Count { get; set; }

    public Supplier Supplier { get; set; }
}

public class Supplier
{
    public int Id { get; set; }

    public string Name { get; set; }
}

Another iteration could be using JsonProperty attribute for Amount and SupplierId property making use of Supplier

class SomeModel
{
    public string Title { get; set; }

    [JsonProperty("count")]
    public double Amount { get; set; }

    public int SupplierId => Supplier.Id;

    public Supplier Supplier { get; set; }
}

class Supplier
{
    public int Id { get; set; }

    public string Name { get; set; }
}

But if you like to stick with your current model you will need to create a custom converter. And what I can suggest you

public class NestedPropertyConverter : JsonConverter
{
    private string[] _properties;

    public NestedPropertyConverter(string propertyChain)
    {
        //todo: check if property chain has valid structure
        _properties = propertyChain.Split('.');
    }

    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType) => true;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = (JToken)serializer.Deserialize(reader);
        foreach (string property in _properties)
        {
            token = token[property];
            if (token == null) //if property doesn't exist
                return existingValue; //or throw exception
        }
        return token.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Basically, the converter allows to bind nested properties. Usage

class SomeModel
{
    public string Title { get; set; }

    [JsonConverter(typeof(NestedPropertyConverter), "id")]
    [JsonProperty("supplier")] //its necessary to specify top level property name
    public int SupplierId { get; set; }

    [JsonProperty("count")]
    public double Amount { get; set; }
}

Note

I also tested the following payload

{
    count : 10,
    supplier : {
        id : 342,
        name : "test",
        test: {
            val: 55
        }
    },
    title : "some title"
}

and config for property

[JsonConverter(typeof(NestedPropertyConverter), "test.val")]
[JsonProperty("supplier")]
public int SupplierId { get; set; }

and it works fine.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.