2

I wonder if it is possible to serialize and deserialize any List<string> as a string of comma separated values in C# using Json.Net:

class MyDTO
{
    public int Age { get; set; } = 25;
    public string Name { get; set; } = "Jon";
    public List<string> Friends { get; set; } = new List<string> {"Jan", "Joe", "Tim"};
}

var serilalized = JsonConvert.SerializeObject(new MyDTO());

//serialized = { "name": "Jon", "age": 25, "friends": "Jan,Joe,Tim" }

var deserialized = JsonConvert.DeserializeObject<MyDTO>(serialized);

//deserialized.Name = "Jon", deserialized.Age = 25, deserialized.Friends = List<string> {"Jan", "Joe", "Tim"}

How could I achieve what the comments in the code reflect?

Implementing a custom JsonConverter is a good solution to this problem, but when I deserialize using the non generic version JsonConvert.DeserializeObject(serialized), the JsonConverter does not have effect and the deserialized value has type JTokenType.String, instead of JTokenType.Array that is the type that I want to get. For example:

JObject obj = (JObject)JsonConvert.DeserializeObject(serilalized);

JToken token = obj["friends"];

Console.WriteLine(token.Type == JTokenType.String); //prints true

Console.WriteLine(token.Type == JTokenType.Array); //prints false

I would like that the code above could print false in the first case and true in the second. Note that while deserializating I don't have the type MyDTO available.

9
  • {age: 25, name: "Jon", friends = "\"Jan\", \"Joe\", \"Tim\""} that's not valid json, so no. Commented Jul 24, 2017 at 14:25
  • I don't understand, you're asking how to generate invalid JSON? Commented Jul 24, 2017 at 14:27
  • 3
    What is wrong with {age: 25, name: "Jon", friends: ["Jan", "Joe", "Tim"] }? (in other words, did you try it and look at what it generated?) Commented Jul 24, 2017 at 14:27
  • sorry my point is not to generate invalid json, i am going to edit the question Commented Jul 24, 2017 at 14:40
  • The serialization does not work because "[\"Jan\",\"Joe\",\"Tim\"]" is a string not a json array. I added the last comment of the question trying to avoid this confusion. Commented Jul 24, 2017 at 14:49

2 Answers 2

8

I'm not sure why you need this, but you can make a simple JsonConverter to do what you want:

public class ListToCsvConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<string>);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(string.Join(",", (List<string>)value));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return new List<string>(((string)reader.Value).Split(','));
    }
}

Then add a [JsonConverter] attribute to the list property in your class that you want to be handled by the converter:

class MyDTO
{
    [JsonConverter(typeof(ListToCsvConverter))]
    public List<string> Friends { get; set; } = new List<string> { "Jan", "Joe", "Tim" };
}

Of course, this solution assumes that none of your strings will have commas in them. If your strings can contain commas, then you would need an escaping mechanism for the commas to ensure a successful round trip. At that point I would recommend just sticking with the default serialization, since JSON already has such a mechanism.

Round-trip demo: https://dotnetfiddle.net/dVh4Zq


If you don't have a DTO available to deserialize into, then you will have to work with a JObject and you won't be able to use the above converter. Instead, you can use a helper method to convert the JToken containing the CSV string to a JArray after deserialization:

static JArray ConvertCsvToArray(JToken value)
{
    if (value.Type == JTokenType.String)
    {
        return JArray.FromObject(((string)value).Split(','));
    }
    throw new ArgumentException("The token does not contain a string value.");
}

Use it like this:

JObject obj = (JObject)JsonConvert.DeserializeObject(serialized);

JArray array = ConvertCsvToArray(obj["friends"]);
Sign up to request clarification or add additional context in comments.

1 Comment

That was my solution, I thought a better one could be possible. Thank you very much @BrianRogers.
0

If you look at serilalized in the debugger, it looks like this:

"{\"Age\":25,\"Name\":\"Jon\",\"Friends\":[\"Jan\",\"Joe\",\"Tim\"]}"

This is because serilalized is a string and that string has embedded " in it.

When printed to the console it looks like this:

{"Age":25,"Name":"Jon","Friends":["Jan","Joe","Tim"]}

Which is exactly correct (Friends is a list of String).

The only problem here is that you initialize Friends and the deserialization adds to that, resulting in 6 entries instead of 3.

If you feel you really need to have Friends serialize to a string of comma separated values (which I would not recommend), you would need to look at JsonConverter.

Update: Note that this does not work for deserializing to JObject (as you've now indicated that you want). You would need to look for strings with commas and .Split those for your destination object (assuming all strings with commas are supposed to be lists of string). OR just serialize like you had before and you get a JArray that has strings. This is exactly why I was not recommending you try and do this: non-standard representation of the data.

2 Comments

I see that you have not understood me. I am going to edit again.
Your code works fine as it is, and generates correct and usable JSON. If you want Friends as a comma separated list, then you need a lot more non standard code and I don't see any point for it if anything else working with the code will use normal JSON libraries.

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.