確認時の.NET Frameworkのバージョン: 4.6.00081
.NET標準のメソッドでJSONを読み込もうとした時、「末尾がコンマで終わる配列」「末尾がコンマで終わるオブジェクト」というJSON textではないものを、方法によっては読める、という記事です(長い)。
("trailing comma"の日本語訳って、何か定着した言い回しはあるのでしょうか。)
JSONの構文
JSONの構文は http://json.org/ のRailroad Diagramが分かりやすいです。
array, objectともに、要素間だけコンマが許されており、末尾コンマは許されていません。
なお RFC4627 の"4. Parsers"、および RFC7159 の"9. Parsers"に次の記述があるので、JSON textではない文字列を受け入れていても、JSONパーサーとして妥当です。
A JSON parser MAY accept non-JSON forms or extensions.
JsonReaderWriterFactory.CreateJsonReaderでは読める
JsonReaderWriterFactory Class は.NET3.5からあるものです。
CreateJsonReader Method で読み込むと、末尾コンマの場合でも正常に読み込めました。
以下確認コード。C#コードの下に、標準出力の内容を記載しています。
末尾コンマ配列版:
using System; using System.Runtime.Serialization.Json; using System.Text; using System.Xml; using System.Xml.Linq; namespace CsConsoleTest { internal class Program { private static void Main(string[] args) { var json = "[1,]"; var buffer = Encoding.UTF8.GetBytes(json); using (var reader = JsonReaderWriterFactory.CreateJsonReader( buffer, XmlDictionaryReaderQuotas.Max)) { var element = XElement.Load(reader); Console.WriteLine(element); } } } }
<root type="array"> <item type="number">1</item> </root>
末尾コンマオブジェクト版:
using System; using System.Runtime.Serialization.Json; using System.Text; using System.Xml; using System.Xml.Linq; namespace CsConsoleTest { internal class Program { private static void Main(string[] args) { var json = @"{""key"":123,}"; var buffer = Encoding.UTF8.GetBytes(json); using (var reader = JsonReaderWriterFactory.CreateJsonReader( buffer, XmlDictionaryReaderQuotas.Max)) { var element = XElement.Load(reader); Console.WriteLine(element); } } } }
<root type="object"> <key type="number">123</key> </root>
JavaScriptSerializer.Deserializeでは例外発生
JavaScriptSerializer Class も同様に.NET3.5からあります。
Deserialize Method で読み込むと、末尾コンマの場合ではArgumentExceptionが送出されました。
以下確認コード。C#コードの下に、例外の詳細を記載しています。
末尾コンマ配列:
using System; using System.Web.Script.Serialization; namespace CsConsoleTest { internal class Program { private static void Main(string[] args) { var json = "[1,]"; var serializer = new JavaScriptSerializer(); var obj = serializer.Deserialize<object>(json); // 例外発生 Console.WriteLine(obj); } } }
ハンドルされていない例外: System.ArgumentException: 無効な配列が渡されました。末尾に余分な ',' があります。 (4): [1,] 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeList(Int32 depth) 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth) 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer) 場所 System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit) 場所 System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input) 場所 CsConsoleTest.Program.Main(String[] args)
末尾コンマオブジェクト:
using System; using System.Web.Script.Serialization; namespace CsConsoleTest { internal class Program { private static void Main(string[] args) { var json = @"{""key"":123,}"; var serializer = new JavaScriptSerializer(); var obj = serializer.Deserialize<object>(json); // 例外発生 Console.WriteLine(obj); } } }
ハンドルされていない例外: System.ArgumentException: 無効な JSON プリミティブです: 。 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializePrimitiveObject() 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth) 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeDictionary(Int32 depth) 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth) 場所 System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer) 場所 System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit) 場所 System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input) 場所 CsConsoleTest.Program.Main(String[] args)