2

I have the same issue as this guy posting single item in json array to .net object.

To summarize, I am sending JSON data to an action and getting null when there is only one item passed. But it works when there is more than one item.

I know this has to do with how the JSON string is constructed (list of object vs. single object) but I have no choice; I am using a limited jquery json library (that's another story)

JSON string example with a list of fields

{  
   "formtemplate":{  
      "fields":{  
         "field":[  
            {  
               "_class":"form-control text-input",
               "_label":"Text Field",
               "_name":"text-1467980984393",
               "_type":"text",
               "_subtype":"text"
            },
            {  
               "_class":"form-control text-input",
               "_label":"Text Field",
               "_name":"text-1467980999418",
               "_type":"text",
               "_subtype":"text"
            }
         ]
      }
   }
}

JSON string example with only one field item

{  
   "formtemplate":{  
      "fields":{  
         "field":{  
            "_class":"form-control text-input",
            "_label":"Text Field",
            "_name":"text-1467980984393",
            "_type":"text",
            "_subtype":"text"
         }
      }
   }
}

Model

public class Fields
{
    public List<Field> field { get; set; }
}

public class Formtemplate
{
    public Fields fields { get; set; }
}

public class CustomFields
{
    public Formtemplate formtemplate { get; set; }
}

public class Field
{
    public string _label { get; set; }
    public string _name { get; set; }
    public string _type { get; set; }
    public List<Option> option { get; set; }
}

Action

[HttpPost]
public JsonResult saveCustomfields(CustomFields customfields)
{

}

I followed the advice on the link provided but it didn't seem to work, here is what I did

public class Fields
{
    private List<Field> field;

    public object Numbers
    {
        get { return field; }
        set
        {
            if (value.GetType() == typeof(Field))
            {
                field = new List<Field> { (Field)value };
            }
            else if (value.GetType() == typeof(List<Field>))
            {
                field = (List<Field>)value;
            }
        }
    }
}

In the action I get both of "Numbers" and "field" as null when there is one item or more. The solution is logical but it didn't work for me.

Update

I researched a bit more and implemented a custom converter from this link How to handle both a single item and an array for the same property using JSON.net. But when I debug; it seems like SingleOrArrayConverter is not even called

public class Fields
{
    [JsonProperty("field")]
    [JsonConverter(typeof(SingleOrArrayConverter<Field>))]
    public List<Field> field { get; set; }
}

public class Formtemplate
{
    [JsonProperty("fields")]
    public Fields fields { get; set; }
}

public class CustomFields
{
    [JsonProperty("formtemplate")]
    public Formtemplate formtemplate { get; set; }
}

public class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Array)
        {
            return token.ToObject<List<T>>();
        }
        return new List<T> { token.ToObject<T>() };
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

But still no success, does it have to do with my action?

Community
  • 1
  • 1
Billy Blaze
  • 107
  • 9
  • try changing `"field"` to `"numbers"` in your json – Khanh TO Jul 08 '16 at 13:45
  • What are you using to deserialize the JSON? If you are using Newtonsoft, you can use property annotations to map properties with keys. If you don't it is assumed that the name of the property is the same as the name of the key in the JSON. – Bhushan Shah Jul 08 '16 at 13:47
  • @Khanh TO, I can't I have to use the original tag names in JSON, I don't have control over that string – Billy Blaze Jul 08 '16 at 13:59
  • @AmateurProgrammer, I don't deserialize, MVC takes care of it – Billy Blaze Jul 08 '16 at 14:00
  • @Billy Blaze: well, then use `public object field` and change `private List field;` to something else. Example: `private List f;` – Khanh TO Jul 08 '16 at 14:00
  • @Billy Blaze: try a custom coverter. I created some sample code in my answer. Not sure if it works, I posted as answer because I cannot include lots of code in comments. – Khanh TO Jul 08 '16 at 14:14
  • @KhanhTO, I tried that but it still gives null every time – Billy Blaze Jul 08 '16 at 14:14
  • Please tell me whether the custom converter works or not – Khanh TO Jul 08 '16 at 14:25
  • I just tested your `SingleOrArrayConverter` in a standalone console test app, and it works perfectly. See https://dotnetfiddle.net/L9KjxB. So there must be some problem in how you are binding your model. Can you give a [mcve] for how you are doing the binding? E.g. are you [inheriting from `Controller` or `ApiController`](https://stackoverflow.com/questions/12730280/posting-a-jobject-to-an-action/12730465#12730465) or something different? See also https://docs.asp.net/en/latest/mvc/models/model-binding.html – dbc Jul 08 '16 at 21:28
  • You passed the json data as a string and then you deserialized it using the converter, I don't doubt it would work, in my case I can't do that because I have the object CustomFields as a parameter in my Action saveCustomfields and inheriting from Controller so I guess I am using the native deserialization instead of the newly implemented converter. It's not even called. So how would you do use this new converter when you're posting data from the view using ajax? – Billy Blaze Jul 08 '16 at 22:17
  • @BillyBlaze - I did not "deserialized it using the converter". Rather, the converter is attached to the `List field` property as an attribute. Thus the converter will always be used by Json.NET. If the converter is not being used, *then Json.NET is not being used to bind your model*. And in fact [this documentation](https://docs.asp.net/en/latest/mvc/models/model-binding.html) suggests that Json.NET is only used for `[FromBody]` parameters when inside a `Controller`. – dbc Jul 08 '16 at 23:10
  • @dbc it's not being used although I added the [FromBody] to the parameter. – Billy Blaze Jul 09 '16 at 09:50
  • @BillyBlaze - then I don't know the answer. However, I suspect that the wrong set of people are looking at this question based on its current wording. I suggest asking a new question like, "how to use Json.NET for model binding in an mvc[3, 4, 5 or whatever] project" and give a [mcve] (much simpler that what you have now) showing it isn't used. In fact, I searched for just that and found https://stackoverflow.com/questions/23995210/how-to-use-json-net-for-json-modelbinding-in-an-mvc5-project – dbc Jul 09 '16 at 19:05
  • Why wouldn't you just send an array of one element? Why mix an object and a collection? – Kenneth K. Jul 09 '16 at 23:40
  • @Kenneth K. you're right, it's simpler in javascript, I found the solution here http://stackoverflow.com/questions/38272396/custom-json-converter-not-being-called-after-posting-data-from-ajax-to-controlle/38290151#38290151 – Billy Blaze Jul 11 '16 at 08:46

0 Answers0