3

I have an object:

class Node
{
  public Node(string text)
  {
    Text = new List<string> { text };
  }

  public List<string> Text { get; set; }
}

When I attempt to round-trip an instance of this object to JSON using Json.NET like so:

var root = new Node("");

var json = JsonConvert.SerializeObject(root);
root = JsonConvert.DeserializeObject<Node>(json);

I get the following error:

Unhandled Exception: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: [. Path 'Text', line 2, position 11.
   at Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadAsString()
   at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)

For some reason it just can't handle that List<string> Text field and I can't for the life of me figure it out.

I'm literally trying to deserialise the string it JUST serialised. I could try write a custom converter but it doesn't seem like it should be necessary here.

Is there perhaps an attribute I can use to help it along?

Edit:

Created a new (.NET Framework Console App) project with just one Program.cs file with the following code in it:

using Newtonsoft.Json; // Version: 12.0.3
using System.Collections.Generic;

namespace ConsoleApp1
{
  class Node
  {
    public Node(string text)
    {
      Text = new List<string> { text };
    }

    public List<string> Text { get; set; }
  }

  class Program
  {
    static void Main()
    {
      var root = new Node("");

      var json = JsonConvert.SerializeObject(root);
      root = JsonConvert.DeserializeObject<Node>(json);
    }
  }
}

Am still getting the same error.

dbc
  • 104,963
  • 20
  • 228
  • 340
alexania
  • 2,395
  • 2
  • 13
  • 11

2 Answers2

3

Your problem is that your Node class does not have a parameterless constructor. Instead, it has a single parameterized constructor. In such a case Json.NET will invoke the parameterized constructor, matching constructor arguments to JSON properties by case-invariant name. Unfortunately, the argument text for the constructor does not match the type of the property Text. Specifically, the argument is a single string while the property is an array of strings:

class Node
{
    public Node(string text /* A single string named text */)
    {
        Text = new List<string> { text };
    }

    public List<string> Text { get; set; } // A collection of strings named Text
}

Since the array of strings in the JSON cannot be deserialized to the single string required to construct your class, you get the exception you are seeing. Demo fiddle #1 here.

To solve this, your options include:

  1. Adding a parameterless constructor. Json.NET will call it by default:

    class Node
    {
        public Node() => Text = new List<string>();
    
        public Node(string text /* A single string named text */)
        {
            Text = new List<string> { text };
        }
    
        public List<string> Text { get; set; } // A collection of strings named Text
    }
    

    It could be private or protected if you mark it with [JsonConstructor].

    Demo fiddle #2 here.

  2. Changing the constructor to accept a collection of strings. params string [] text would seem to be easiest:

    class Node
    {
        public Node(params string [] text)
        {
            Text = text.ToList();
        }
    
        public List<string> Text { get; set; } // A collection of strings named Text
    }
    

    Demo fiddle #3 here.

  3. Renaming the text argument in the constructor to something different, say singletext, is less than ideal because Json.NET will pass null for the missing constructor value, which will get added initially to the Text collection and will remain there unless the collection is subsequently replaced during deserialization.

    Demo fiddle #4 here.

Related reading:

dbc
  • 104,963
  • 20
  • 228
  • 340
-1

So I solved the problem by renaming the field from Text to DialogueText, renaming it to Texts also solves it.

Since I had already tried to set the name via JsonProperty with no luck, my current assumption is that perhaps Text is a reserved field name that is hard coded to expect a string.

alexania
  • 2,395
  • 2
  • 13
  • 11