Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array with missing [ is considered a valid JSON #2585

Closed
MangelMaxime opened this issue Sep 11, 2021 · 3 comments
Closed

Array with missing [ is considered a valid JSON #2585

MangelMaxime opened this issue Sep 11, 2021 · 3 comments

Comments

@MangelMaxime
Copy link

Source/destination types

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
					
public class Program
{
	public static void Main()
	{
		var json = "\"SomeUnion\",\"foo\"]\"";
		var reader = new JsonTextReader(new StringReader(json)) {
			DateParseHandling = DateParseHandling.None
		};
		
		var res = JValue.ReadFrom(reader);
		
		Console.WriteLine(json);
		Console.WriteLine(res.ToString());
	}
}

Source/destination JSON

"SomeUnion","foo"]"

Expected behavior

I am expecting an error similar to:

Unhandled exception. Newtonsoft.Json.JsonReaderException: Unterminated string. Expected delimiter: ". Path '', line 1, position 8.
   at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)
   at Newtonsoft.Json.JsonTextReader.ParseString(Char quote, ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ParseValue()
   at Newtonsoft.Json.Linq.JToken.ReadFrom(JsonReader reader, JsonLoadSettings settings)
   at Newtonsoft.Json.Linq.JToken.ReadFrom(JsonReader reader)
   at Program.Main()
Command terminated by signal 6

Because according to https://jsonformatter.curiousconcept.com/# it is not a valid JSON:

image

Actual behavior

No error

Steps to reproduce

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
					
public class Program
{
	public static void Main()
	{
		var json = "\"SomeUnion\",\"foo\"]\"";
		var reader = new JsonTextReader(new StringReader(json)) {
			DateParseHandling = DateParseHandling.None
		};
		
		var res = JValue.ReadFrom(reader);
		
		Console.WriteLine(json);
		Console.WriteLine(res.ToString());
	}
}
@elgonzo
Copy link

elgonzo commented Sep 15, 2021

Not really a bug...

There is no json array to be considered. The ] is a red herring. There is a value there that is a valid string according to JSON syntax rules. And that string value "SomeUnion" is what JToken.ReadFrom reads, hence no error. (Btw, there is no specific JValue.ReadFrom implementation; the compiler will simply resolve JValue.ReadFrom to JToken.ReadFrom).

That JToken.ReadFrom should attempt reading an JSON array when confronted with your "json" string is a misplaced expectation. It reads whatever token (or JSON data type, if you will) it detects, which in this case is just the JSON string "SomeUnion", and it doesn't care whatever else follows after that token. In this sense, "SomeUnion","foo"]" would be no different than for example "SomeUnion"this is totally not json anymore... whether there is some bracket in the "tail" after "SomeUnion" or not.

If you expect your JSON data to be an array, you can attempt to load it as a JArray by using the JArray.Load method, like:

var res = JArray.Load(reader);

This will then also throw an exception if no valid JSON array can be obtained from the reader.
If using a JsonReader is not a strict requirement, you could alternatively also use the JToken.Parse(string) method, which consumes the entire json string and not just whatever starting portion forming a valid JSON data type like JToken.ReadFrom does.

If all you want is to see whether there is additional text following after the read json value, you could also just call reader.Read() after calling JToken.ReadFrom.

@elgonzo
Copy link

elgonzo commented Sep 15, 2021

If the reason you are using a JsonReader is to configure said JsonReader (and this excluding you from using JToken.Parse) while expecting to read varying input data that is not always an Json array, there is a fourth way of doing this that avoids the explicit call of reader.Read(): using the JsonSerializer. JsonSerializer can actually deserialize into a JToken. The deserializer figures out the concrete JTtoken type (such as JValue, JArray, JObject) by itself:

var serializationSettings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    CheckAdditionalContent = true                     // <--- THIS IS VERY IMPORTANT, DO NOT FORGET TO SET THIS
};
var serializer = JsonSerializer.Create(serializationSettings);

var reader = new JsonTextReader(new StringReader(json));
var res = serializer.Deserialize<JToken>(reader);

Note that it is required for your use case to set CheckAdditionalContent in the serialization settings to true, otherwise this approach using the deserializer would just work like JToken.ReadFrom (i.e., just reading "SomeUnion"). Dotnet fiddle example: https://dotnetfiddle.net/m1yAM2

@MangelMaxime
Copy link
Author

Thank you for the explanations @elgonzo .

Your last answer is what solved my problem, as I don't know what is the form of the JSON in advance and so I don't know if I am expecting a string, object or an array, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants