locked
Deserialize JSON With Delimited String Directly to List Using TypeConverter RRS feed

  • Question

  • User-691759321 posted

    So, I have a JSON value as a string:

    "[{"Purpose":"Internal; Product Selection"},{"Purpose":"Product Selection; Web"},{"Purpose":"Internal; Product Selection"}]"

    I have a class designed to deserialize the JSON to (note this is a simplified class structure).

    using Newtonsoft.Json;
    using System.Collections.Generic;
    using System.ComponentModel;
    
    public class Attribute
    {
    	public string Purpose { get; set; }
    }

    I am using Newtonsoft.Json version 9.  Each Attribute.Purpose field is populated with semi-colon delimited list after the deserialization process.  What I would prefer is to have Purpose be a List<string>.  I did try to take in the value as a string and have a read-only property convert the string to a list.  This works but is slow. 

    using Newtonsoft.Json;
    using System.Collections.Generic;
    using System.Linq;
    
    public class Attribute
    {
    
        [JsonProperty("Purpose")]
        public string _Purpose { get; set; }
    
        [JsonIgnore]
        public List<string> Purpose {
            get
            {
                return this._Purpose.Split(';').Select(x => x.Trim()).ToList();
            }
        }
    }

    I would like to use a TypeConverter but cannot seem to find one that work.  I should mention that I am using Sitecore through a Solr ContentSearchManager query.  Sitecore has provided some custom converters for index fields which do exactly what I want, however this is not an index field. The converter I use on the index field is Sitecore.ContentSearch.Converters.IndexFieldEnumerableConverter.  I am looking for some way to handle this with standard .NET libraries or Newtonsoft.

    Any ideas?

    Friday, May 22, 2020 3:35 PM

Answers

  • User753101303 posted

    You can put this attribute on the Purpose property only. For example :

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleDemo
    {
        public class DelimitedStringConverter : JsonConverter<List<string>>
        {
            public override void WriteJson(JsonWriter writer, List<string> value, JsonSerializer serializer)
            {
                throw new NotImplementedException();
            }
            public override List<string> ReadJson(JsonReader reader, Type objectType, List<string> existingValue, bool hasExistingValue, JsonSerializer serializer)
            {
                string s = (string)reader.Value;
                if (string.IsNullOrEmpty(s))
                {
                    return new List<string>();
                }
                return s.Split(';').Select(x => x.Trim()).ToList();
            }
        }
    
        class Program
        {
            class Data
            {
                public string Name { get; set; }
                [JsonConverter(typeof(DelimitedStringConverter))]
    public List<string> Purpose { get; set; }
    } static void Main() { var str = @"{""Name"":""a; b"",""Purpose"":""a; b""}"; var data = JsonConvert.DeserializeObject<Data>(str); Console.WriteLine(data.Name); foreach (string value in data.Purpose) { Console.WriteLine("-{0}-", value); } } } }

    shows :

    a; b
    -a-
    -b-

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, May 24, 2020 9:02 AM

All replies

  • User475983607 posted

    The ideal solution is fixing the JSON format.

    {"Purpose": ["Internal", "Product Selection"]}

    Friday, May 22, 2020 3:44 PM
  • User-691759321 posted
    The simple response is that changing the format of Json is not an option. I know it is improper for the data type but I do not have control to fix it.
    Friday, May 22, 2020 3:49 PM
  • User753101303 posted

    Hi,

    From a quick look it seems you could customize how this property is serialized/deserialized using https://www.newtonsoft.com/json/help/html/CustomJsonConverterGeneric.htm doing a string.Split when reading and a string.Join when writing.

    Saturday, May 23, 2020 8:29 PM
  • User-691759321 posted

    While an interesting idea I am not sure how this would translate to my example.  The code in that link seems to apply for custom classes only.  Almost every field in my Product class is a string.  I am not sure if a custom converter would know how to distinguish between a delimited list and a description that just happens to contain the same delimiter.  

    Saturday, May 23, 2020 9:24 PM
  • User753101303 posted

    You can put this attribute on the Purpose property only. For example :

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleDemo
    {
        public class DelimitedStringConverter : JsonConverter<List<string>>
        {
            public override void WriteJson(JsonWriter writer, List<string> value, JsonSerializer serializer)
            {
                throw new NotImplementedException();
            }
            public override List<string> ReadJson(JsonReader reader, Type objectType, List<string> existingValue, bool hasExistingValue, JsonSerializer serializer)
            {
                string s = (string)reader.Value;
                if (string.IsNullOrEmpty(s))
                {
                    return new List<string>();
                }
                return s.Split(';').Select(x => x.Trim()).ToList();
            }
        }
    
        class Program
        {
            class Data
            {
                public string Name { get; set; }
                [JsonConverter(typeof(DelimitedStringConverter))]
    public List<string> Purpose { get; set; }
    } static void Main() { var str = @"{""Name"":""a; b"",""Purpose"":""a; b""}"; var data = JsonConvert.DeserializeObject<Data>(str); Console.WriteLine(data.Name); foreach (string value in data.Purpose) { Console.WriteLine("-{0}-", value); } } } }

    shows :

    a; b
    -a-
    -b-

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, May 24, 2020 9:02 AM
  • User-691759321 posted

    I could not use JsonConverter<List<string>> because we are still using version 9 of Newtonsoft.Json and it does not support it.  I was able to take that out and use the generic JsonConverter override to achieve what I wanted.  

    Thanks!

    Tuesday, May 26, 2020 9:48 PM