1

I am trying to submit a json to an API method that takes a model with some IEnumerables, but the IEnumerable members are always coming through as null. At this point the controller doesn't do anything, I'm just using a breakpoint to see if the data is coming though.

My json:

{
    "apiModel": {
        "Results":[ 
            {
                "ActivityIdentifier":"abc",
                "CharacteristicName": "Dissolved Oxygen",
                "SubmitData": true
            },
            {
                "ActivityIdentifier":"efg", 
                "CharacteristicName": "pH",
                "SubmitData": true
            }
        ]
    }
}

My api controller:

[HttpPost]
    public void Post([FromBody]APIIncomingViewModel apiModel)
    {
        IEnumerable<IncomingResult> results = apiModel.Results;
       // IEnumerable<Activity> activity = apiModel.Activities;
    }

My model:

namespace MyNameSpace.Models
{
    public class APIIncomingViewModel
    {
        //public IEnumerable<Activity> Activities { get; set; }

        public IEnumerable<IncomingResult> Results { get; set; }
    }
}

Results model:

namespace MyNameSpace.Models
{
    public class IncomingResult
    {
        public string ActivityIdentifier { get; set; }

        public string CharacteristicName { get; set; }

        public bool SubmitData { get; set; }
    }
}

I'm getting an apiModel object, but the Results member is null. I would eventually like to have other IEnumerables in there like Activities.

I have tried making Results an IncomingResult[], and I've tried a lot of different forms for the json, but nothing seems to change - Results is always null.

In this thread I saw it mentioned that you might have to put it inside an anonymous object, but I'm not sure how to handle it if I'm ultimately posting both Results and Activities, and I don't plan on using jQuery's ajax in any case - this will be coming from a mobile app. Right now I'm just posting the json using Postman. I'm also not sure why it didn't work for me as an array.

Any help in figuring out the correct format would be much appreciated.

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
Ella
  • 465
  • 1
  • 5
  • 14

1 Answers1

1

You're JSON is describing an object model that one level deeper than you think it is. It describes an object with a property named ApiModel. That property contains an array of Result objects.

Bear with me on this explanation, it's a little gnarly to wrap your head around at first but I promise it's leading somewhere. ;)

Let's break down your JSON by the objects it describes:

  1. Your first object is the JSON itself. This is the "root object". Everything contained between the { and } are properties of this root object:

    {
    
    }
    
  2. Next is your apiModel object. This is a property of your root object.

    {
        "apiModel": {}
    }
    
  3. Next, your apiModel object contains a single array named Results:

    {
        "apiModel": {
            "Results":[]
        }
    }
    
  4. Finally, the Results array contains a collection of Result objects:

    {
        "apiModel": {
            "Results":[ 
                {
                    "ActivityIdentifier":"abc",
                    "CharacteristicName": "Dissolved Oxygen",
                    "SubmitData": true
                },
                {
                    "ActivityIdentifier":"efg", 
                    "CharacteristicName": "pH",
                    "SubmitData": true
                }
            ]
        }
    }
    

Your problem arises because you're not providing a root object. So when you tell the deserializer you want to convert the incoming JSON to your APIIncomingViewModel, the first thing it does is try and populate the apiModel property of the APIIncomingViewModel. Since this doesn't exist, it never attempts to deal with the Results property of the apiModel object.

There are two potential fixes:

  1. Change your JSON to match APIIncomingViewModel:

    {
        "Results": [{
                "ActivityIdentifier": "abc",
                "CharacteristicName": "Dissolved Oxygen",
                "SubmitData": true
            },
            {
                "ActivityIdentifier": "efg",
                "CharacteristicName": "pH",
                "SubmitData": true
            }
        ]
    }
    
  2. Provide an object model that matches your JSON:

    public class Result
    {
        public string ActivityIdentifier { get; set; }
        public string CharacteristicName { get; set; }
        public bool SubmitData { get; set; }
    }
    
    public class ApiModel
    {
        public List<Result> Results { get; set; }
    }
    
    public class RootObject
    {
        public ApiModel apiModel { get; set; }
    }
    
Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
  • 1
    Thanks! #1 worked for me. One thing that I am still a little confused about - why would the debugger show something in the apiModel parameter if it doesn't match? I had it set to just results initially (although I'm guessing I had a different syntax error then), and it didn't show anything in the debugger until I put everything inside an "apiModel" in the json. How was that able to bind at all? – Ella Apr 04 '18 at 22:47
  • It's hard to tell, it could be a number of things. Generally, it comes down to the property described by the `json` not matching a property name in your object. Deserialization isn't a very intelligent process, it will happily ignore anything it struggles with. Some folks have attempted to put together smarter serialization engines but that intelligence always comes at the expense of performance. – Marc LaFleur Apr 04 '18 at 22:56