How to deserialize dodgy JSON (with improperly quoted strings, and missing brackets)?
Answering your questions #1 - #3 in order:
-
Json.NET does not support reading dodgy property values in the form
colors["Open"]
(which, as you correctly note, violates the JSON standard).Instead, you will need to manually fix these values, e.g. through some sort of
Regex
:var regex = new Regex(@"(colors\[)(.*)(\])"); var fixedJsonString = regex.Replace(jsonString, m => string.Format(@"""{0}{1}{2}""", m.Groups[1].Value, m.Groups[2].Value.Replace("\"", "\\\""), m.Groups[3].Value));
This changes the
color
property values into properly escaped JSON strings:color: "colors[\"Open\"]"
Json.NET does, however, have the capability to write dodgy property values by calling
JsonWriter.WriteRawValue()
from within a customJsonConverter
.Define the following converter:
public class RawStringConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(string); } public override bool CanRead { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var s = (string)value; writer.WriteRawValue(s); } }
Then define your
RootObject
as follows:public class RootObject { public string name { get; set; } public string id { get; set; } public string status { get; set; } [JsonConverter(typeof(RawStringConverter))] public string color { get; set; } }
Then, when re-serialized, you will get the original dodgy values in your JSON.
-
Support for deserializing comma-delimited JSON without outer brackets will be in the next release of Json.NET after 10.0.3. see Issue 1396 and Issue 1355 for details. You will need to set
JsonTextReader.SupportMultipleContent = true
to make it work.In the meantime, as a workaround, you could grab
ChainedTextReader
andpublic static TextReader Extensions.Concat(this TextReader first, TextReader second)
from the answer toHow to string multiple TextReaders together?
by Rex M and surround your JSON with brackets[
and]
.Thus you would deserialize your JSON as follows:
List<RootObject> list; using (var reader = new StringReader("[").Concat(new StringReader(fixedJsonString)).Concat(new StringReader("]"))) using (var jsonReader = new JsonTextReader(reader)) { list = JsonSerializer.CreateDefault().Deserialize<List<RootObject>>(jsonReader); }
(Or you could just manually surround your JSON string with
[
and]
, but I prefer solutions that don't involve copying possibly large strings.)Re-serializing a root collection without outer braces is possible if you serialize each item individually using its own
JsonTextWriter
withCloseOutput = false
. You can also manually write a,
between each serialized item to the underlyingTextWriter
shared by everyJsonTextWriter
. -
Serializing JSON property names without a surrounding quote character is possible if you set
JsonTextWriter.QuoteName = false
.Thus, to re-serialize your
List<RootObject>
without quoted property names or outer braces, do:var sb = new StringBuilder(); bool first = true; using (var textWriter = new StringWriter(sb)) { foreach (var item in list) { if (!first) { textWriter.WriteLine(","); } first = false; using (var jsonWriter = new JsonTextWriter(textWriter) { QuoteName = false, Formatting = Formatting.Indented, CloseOutput = false }) { JsonSerializer.CreateDefault().Serialize(jsonWriter, item); } } } var reserializedJson = sb.ToString();
Sample .Net fiddle showing all this in action.