0

I have some JSON data, which I wish to deserialize into a C# object.

The data looks like this:

{
    "version": 0,
    "guid": "2166a7d5744d47009adaa29f0e549696",
    "csv": "one:1,two:2,three:3",
    "method": "substract"
}

The "csv" property is giving me trouble, as I want it to be deserialized to the following csv object (it's content can change, so it can have more or less comma-separated values):

public class CSV
{
    public int One {get;set;}
    public bool Two {get;set;}
    public int Three {get;set;}
}

I already have an object for the bigger JSON:

public class MyJsonData
{
    public int Version {get;set;}
    public string Guid {get;set;}
    public string Csv {get;set;}
    public string Method {get;set;}
}

But I just can't figure out how to parse the comma-separated string to my CSV object.

I tried adding a public CSV (string s) constructor to the CSV object and changing the type from string to CSV in the MyJsonData object, hoping C# would use that constructor (I'm using System.Text.Json) but it didn't and it threw an exception.

The only thing I can come up is separating the string by commas and using reflection to fill the CSV object properties (I've defined all possible values the string can have and expect the missing ones to be empty or have the default value for the type). But I'm looking for a more elegant solution, as I am accessing the MyJsonData object everywhere in my code, so having to separately handle a CSV object seems cumbersome and a worst-practice.

Is there any way to tell JSON what method to use to convert the string to my CSV object when deserializing?

notarobot
  • 119
  • 5
  • "Elegant" is not a testable attribute of an answer on Stack Overflow. [Microsoft provides documentation on how to create custom converters for System.Text.Json](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-7-0) There are also many questions which detail their use; e.g. [How can I create a custom converter similar to Newtonsoft using system.text.json?](https://stackoverflow.com/q/72396741/215552) – Heretic Monkey Dec 23 '22 at 20:30
  • Your json is not valid. Can you post the valid one pls? – Serge Dec 23 '22 at 21:22
  • @Serge, sorry missed a quote character, it should be fine, now. – notarobot Dec 23 '22 at 21:45
  • @HereticMonkey, thanks for the links! I was using abstract thought to convey a deeper meaning through context or "reading between the lines", as it is also called. I thought it was as obvious as can be, but I will try to make it much, much simpler next time. – notarobot Dec 23 '22 at 21:47
  • @notarobot Are you determined to use Text.Json? Using Newtonsonft.Json will make the code much more simplle – Serge Dec 23 '22 at 22:56
  • @Serge, how so? I thought they are almost identical. – notarobot Dec 23 '22 at 23:05

1 Answers1

1

If you use Newtonsoft.Json you will need only one more string of code

using Newtonsoft.Json;

MyJsonData myJsonData = JsonConvert.DeserializeObject<MyJsonData>(json);

public class MyJsonData
{
    public CSV Csv { get; set; }
    //...another properties

    [JsonConstructor]
    public MyJsonData(string csv)
    {
        Csv = new JObject(csv.Split(",")
                              .Select(i => i.Split(":"))
                              .Select(r => new JProperty(r[0].ToString(), Convert.ToInt32(r[1]))))
                              .ToObject<CSV>();
    }
}

If you use Text.Json with Net 6+, the shortest code probably is

 var jDoc = JsonNode.Parse(json);

jDoc["csv"] =  new JsonObject( JsonDocument.Parse(json).RootElement
                  .GetProperty("csv").GetString()
                  .Split(",")
                  .Select(r => r.Split(":") )
                  .Select(i=>  KeyValuePair.Create<string, JsonNode>(i[0], Convert.ToInt32(i[1]))));

MyJsonData myJsonData = jDoc.Deserialize<MyJsonData>( new JsonSerializerOptions {PropertyNameCaseInsensitive = true});  

if you want one line of code you will probably wrap this code in a custom formater ( as it is usually in the most cases when you use Text.Json)

UPDATE

if some of CSV properties are boolean, you can try this code

Csv = new JObject(csv.Split(",")
                              .Select(i => i.Split(":"))
                              .Select(r => new JProperty(r[0].ToString(),
       r[1].ToLower() =="true"||r[1].ToLower() =="false"? Convert.ToBoolean( r[1]):
       Convert.ToInt32(r[1]))))
                              .ToObject<CSV>();
Serge
  • 40,935
  • 4
  • 18
  • 45
  • Ah, I see my mistake - when I made a constructor I named the string parameter "s" but the name matters. Thank you! Also Merry Christmas! :) Just one more thing, if I were to change the type of CSV's property "Two" to be `bool` how should I change the Newtonsoft code? – notarobot Dec 25 '22 at 18:59
  • You are wonderful but, sorry, I should've been more clear that I meant that in the JSON the 1 or 0 values of the "Two" property should be regarded as "true" and "false" and, thus, the CSV class would have a `bool` "Two" property, as I will edit in my question. – notarobot Dec 25 '22 at 19:49
  • @notarobot I think it will work any way. You have to try and tell me if you have any problem – Serge Dec 25 '22 at 21:41
  • Yup, it did. I did not believe it but holy smokes, I love C#!!! Thank you a million! And happy holidays! :) – notarobot Dec 26 '22 at 11:05
  • @notarobot Happy Xmas and New Year too! – Serge Dec 26 '22 at 13:31