4

I have a JSON String that I'm trying to deserialize to C# in one swoop.

The Children nodes of SalesLines is a string representation. I want objects all the way down when I deserialize. What is the best way to this with JSON.NET?

{
   "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\"}]}]"
      }
   ]
}
6
  • 1
    If your JSON were well-formed, you could apply EmbeddedLiteralConverter<List<SalesLine>> from How do I convert an escaped JSON string within a JSON object? to your SalesLines property. Commented May 14, 2019 at 18:53
  • I've updated to include valid json Commented May 14, 2019 at 19:16
  • 1
    OK, then does applying [JsonConverter(typeof(EmbeddedLiteralConverter<List<SalesLine>>)] to public List<SalesLine> SalesLines { get; set; } do the job? Commented May 14, 2019 at 19:20
  • 1
    Why have you got this string representation, i.e. JSON within JSON? That's an odd thing to do. Are you able to alter the code which generates this data to do it in a better way? That would be the ideal solution Commented May 14, 2019 at 19:25
  • @ADyson agreed this is one I don't have control over unfortunately. Commented May 14, 2019 at 19:26

2 Answers 2

4

The value of your SalesLines property is double-serialized JSON: a string value that contains JSON embedded as a string literal. You would like to deserialize its contents to a final data model in one step.

To see what the data model should look like, you can unescape the JSON as follows:

var json = JToken.Parse(jsonString);

foreach(var token in json.SelectTokens("value[*].SalesLines").ToList())
{
    token.Replace(JToken.Parse((string)token));
}

Console.WriteLine(json);

Then use one of the code-generation tools mentioned in How to auto-generate a C# class file from a JSON object string to generate a data model from the unescaped JSON (I used http://json2csharp.com/):

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; }
    public List<SalesLine> SalesLines { get; set; }
}

public class RootObject
{
    public List<Value> value { get; set; }
}

Finally, apply EmbeddedLiteralConverter<List<SalesLine>> from this answer to How do I convert an escaped JSON string within a JSON object? to Value:

public class Value
{
    public string documentType { get; set; }
    public string SONumber { get; set; }

    [JsonConverter(typeof(EmbeddedLiteralConverter<List<SalesLine>>))]
    public List<SalesLine> SalesLines { get; set; }
}

Now you will be able to deserialize the JSON to RootObject directly:

root = JsonConvert.DeserializeObject<RootObject>(jsonString);

Demo fiddle here.

Sign up to request clarification or add additional context in comments.

4 Comments

EXACTLY what I was looking for. Thx!
Is using this Literal Converter more or equally as performant as manually deserializing in a loop?
@aherrick - It avoids loading the entire JSON into a JToken hierarchy upfront, so memory impact should be minimized. (Also, using dynamic rather than explicit types such as JToken or RootObject adds a performance penalty.) But you really have to test it yourself, see ericlippert.com/2012/12/17/performance-rant and also stackify.com/top-11-json-performance-usage-tips, especially #6.
newtonsoft.com/json/help/html/Performance.htm#ManuallySerialize states that The absolute fastest way to read and write JSON is to use JsonTextReader/JsonTextWriter directly to manually serialize types. However doing this is quite inconvenient so I wouldn't recommend it unless you absolutely must.
-2

Create a class that will store your SalesLines, another class that will store ItemAttributes.

SalesLines class should have a property List.

Create a SalesOrder class that will have List as one of the properties.

Then you should be able to deserialize into SalesOrder.

2 Comments

This doesn't really solve OP's problem, which is that the value of "SalesLines" is a string containing embedded JSON objects, and they want to directly deserialize that string to a data model.
It does solve the op's problem. The idea is the same as what you wrote above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.