0

In one of my WebAPI 2 applications, I'm having trouble deserializing a List<string> property of a FromBody object. (The list stays empty, while the other properties are deserialized correctly.)

Whatever I do, the property only seems to deserialize correctly if I change the property to a string[]. Unfortunately for me, the property needs to be of type List<string>.

According to another question I found, I should be able to deserialize to a List<T>, as long as T is not an Interface.

Is there anyone who has an idea what I could be doing wrong?

Controller:

public class ProjectsController : ApiController
{
    public IHttpActionResult Post([FromBody]Project project)
    {
        // Do stuff...
    }
}

Project object class:

public class Project
{
    public string ID { get; set; }
    public string Title { get; set; }
    public string Details { get; set; }

    private List<string> _comments;
    public List<string> Comments 
    { 
        get
        {
            return _comments ?? new List<string>();
        }
        set
        {
            if (value != _comments)
                _comments = value;
        } 
    }

    public Project () { }

    // Other methods
}

Request JSON:

{
    "Title": "Test",
    "Details": "Test",
    "Comments":
    [
        "Comment1",
        "Comment2"
    ]
}
DylanVB
  • 187
  • 1
  • 14
  • 1
    Why do you need to recreate the list instance? `Comments` should probably simply be `public List Comments { get; set; }` – vc 74 Feb 28 '18 at 10:49
  • @vc74 The problem does indeed have something to do with creating a new instance. The reason for that piece of code is that I don't want to be able to get a `null` version of `Comments`. – DylanVB Feb 28 '18 at 11:13
  • then you can: `List Comments { get; set; } = new List()` – vc 74 Feb 28 '18 at 11:14
  • `Comments` will still return `null` if it is set to `null` later in the code. – DylanVB Feb 28 '18 at 11:19
  • if it is really a requirement, add code to your setter to prevent setting a null value – vc 74 Feb 28 '18 at 11:23
  • 1
    When you expose any sort of collection through a public property, please never, ever allow it to 1) be `null` 2) be set to `null` from the outside. Make the setter private and you'll never have to worry about the collection being null. Null collections are evil and should be killed with fire. Initialize them in the constructor, or right where you declare them. – s.m. Feb 28 '18 at 11:25
  • Additionally, hell will freeze over before a few collections that get initialized and not used will cause a performance problem. – s.m. Feb 28 '18 at 11:26
  • @s.m. "Make the setter private" and you'll prevent the JSON serializer to work properly – vc 74 Feb 28 '18 at 11:27
  • @vc74 are we talking about JSON.NET? Because in that case there are multiple ways to make it work even with private setters. – s.m. Feb 28 '18 at 11:30

2 Answers2

1

Did you try this?

public class Project
{
    public List<string> Comments {get; set;}
    public Project () 
    { 
        Comments = new List<string>();
    }
    ...
}
IgorM
  • 31
  • 7
  • Not creating a new `List` when `Comments` is `null` indeed solves the issue. The problem with this code is that, in this way, `Comments` can still return `null` if it has been reset, which I would prefer to avoid. – DylanVB Feb 28 '18 at 11:15
  • @DylanVB when you expose any sort of collection through a public property, please never allow it to 1) be `null` 2) be set to `null` from the outside. Make the setter private and you'll never have to worry about the collection being null. Null collections are evil, please kill them any time you can. Initialize them in the constructor, or right where you declare them. – s.m. Feb 28 '18 at 11:22
0

With thanks to @vc74 and @s.m. , I managed to update my project object class to look like the following to make it work the way I want it to:

public class Project
{
    public string ID { get; set; }
    public string Title { get; set; }
    public string Details { get; set; }

    private List<string> _comments = new List<string>();
    public List<string> Comments 
    { 
        get
        {
            return _comments;
        }
        set
        {
            if (value != _comments)
            {
                if (value == null)
                    _comments = new List<string>();
                else
                    _comments = value;
            }
        } 
    }

    public Project () { }

    // Other methods
}

Instead of trying to prevent getting a null value from Comments, I had to prevent setting the value to null.

DylanVB
  • 187
  • 1
  • 14