CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NUnit.Framework; // Throws NUnit.Framework.AssertionException
//https://stackoverflow.com/questions/56136560/json-net-c-sharp-deserialize-json-string-and-children-string
public class ItemAttribute
{
public string AttibuteName { get; set; }
public string AttributeValue { get; set; }
}
public class SalesLine
{
public string SONumber { get; set; }
public int LineNum { get; set; }
public string ItemId { get; set; }
public List<ItemAttribute> ItemAttributes { get; set; }
}
public class Value
{
public string documentType { get; set; }
public string SONumber { get; set; }
[JsonConverter(typeof(EmbeddedLiteralConverter<List<SalesLine>>))]
public List<SalesLine> SalesLines { get; set; }
}
public class RootObject
{
public List<Value> value { get; set; }
}
public class EmbeddedLiteralConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var contract = serializer.ContractResolver.ResolveContract(objectType);
if (contract is JsonPrimitiveContract)
throw new JsonSerializationException("Invalid type: " + objectType);
if (existingValue == null)
existingValue = contract.DefaultCreator();
if (reader.TokenType == JsonToken.String)
{
var json = (string)JToken.Load(reader);
using (var subReader = new JsonTextReader(new StringReader(json)))
{
// By populating a pre-allocated instance we avoid an infinite recursion in EmbeddedLiteralConverter<T>.ReadJson()
// Re-use the existing serializer to preserve settings.
serializer.Populate(subReader, existingValue);
}
}
else
{
serializer.Populate(reader, existingValue);
}
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
class TestClass
{
public static void Test()
{
TestConverter();
ShowUnescapedJson();
}
static void TestConverter()
{
var jsonString = GetJson();
var root = JsonConvert.DeserializeObject<RootObject>(jsonString);
var json2 = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine("\nDeserialized and re-serialized {0}", root);
Console.WriteLine(json2);
}
public static void ShowUnescapedJson()
{
Console.WriteLine("\nUnescaped JSON: ");
var jsonString = GetJson();
var json = JToken.Parse(jsonString);
foreach(var token in json.SelectTokens("value[*].SalesLines").ToList())
{
token.Replace(JToken.Parse((string)token));
}
Console.WriteLine(json);
}
static string GetJson()
{
var json = @"
{
""value"":[
{
""documentType"":""Quote"",
""SONumber"":""S-QUO1001"",
""SalesLines"":""[{\""SONumber\"":\""S-QUO1001\"",\""LineNum\"":10000,\""ItemId\"":\""1936-S\"",\""ItemAttributes\"":[{\""AttibuteName\"":\""COLOR\"",\""AttributeValue\"":\""YELLOW\""},{\""AttibuteName\"":\""DEPTH\"",\""AttributeValue\"":\""100\""},{\""AttibuteName\"":\""WIDTH\"",\""AttributeValue\"":\""120\""},{\""AttibuteName\"":\""HEIGHT\"",\""AttributeValue\"":\""115\""},{\""AttibuteName\"":\""MATERIAL DESCRIPTION\"",\""AttributeValue\"":\""COTTON, WOOD LEGS\""},{\""AttibuteName\"":\""MODEL YEAR\"",\""AttributeValue\"":\""1940\""}]}]""
}
]
}
";
return json;
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
}