2

I need to serialize/deserialize an object to json by using JSON.NET in my application. Object has a property type of ArrayList which contains string arrays. To simulate it I've written the following unit test;

public class JsonTests
{
    public class MyTestClass
    {
        public ArrayList Items { get; private set; }

        public MyTestClass()
        {
            this.Items = new ArrayList();
        }
    }
    [Fact]
    public void JsonConvert_Should_Serialize_ArrayList_And_Deserialize_Successfully()
    {
        MyTestClass myObject = new MyTestClass();
        myObject.Items.Add(new string[] { "Test1", "Test2" });
        string jsonResult = JsonConvert.SerializeObject(myObject);            
        MyTestClass tempMyObject = JsonConvert.DeserializeObject<MyTestClass>(jsonResult);
         //(tempMyObject.Items[0] as string[]) casts to null therefore it throws exception
        Assert.Equal((myObject.Items[0] as string[])[0], (tempMyObject.Items[0] as string[])[0]);
    } 
}

It does not throw any exception durion serialization. However, it does not deserialize ArrayList properly.

My question is , how can I deserialize it back to ArrayList of string arrays ?

Update: In addition, I cannot change the class definition. The class is implemented in an assembly where I cannot edit the class.

dbc
  • 104,963
  • 20
  • 228
  • 340
naltun
  • 114
  • 1
  • 2
  • 12
  • Do you know in advance that the JSON will contain a jagged 2d array of strings, or do you need to infer the `ArrayList` item types from the JSON? – dbc Jan 10 '17 at 22:09

2 Answers2

2

ArrayList is a non-generic, untyped collection so you need to inform Json.NET of the expected type of the items. One way to do this is with a custom JsonConverter for the ArrayList:

public class ArrayListConverter<TItem> : JsonConverter
{
    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var list = serializer.Deserialize<List<TItem>>(reader);
        var arrayList = existingValue as ArrayList ?? new ArrayList(list.Count);
        arrayList.AddRange(list);
        return arrayList;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ArrayList);
    }
}

Then apply the converter to the class as follows:

public class MyTestClass
{
    [JsonConverter(typeof(ArrayListConverter<string []>))]
    public ArrayList Items { get; private set; }

    public MyTestClass()
    {
        this.Items = new ArrayList();
    }
}

Sample fiddle.

If the class cannot be modified, and you want all instances of ArrayList in your object graph to deserialize their items as string [], you can add the converter to JsonSerializerSettings.Converters instead of adding it to the type:

var tempMyObject = JsonConvert.DeserializeObject<MyTestClass>(jsonResult,
                                                              new JsonSerializerSettings { Converters = { new ArrayListConverter<string []>() } });

Sample fiddle #2.

And finally, if the class cannot be modified and you only want the specific ArrayList Items property inside MyTestClass to have its items deserialized as string [], you will need to create a custom converter for MyTestClass. You can use the pattern from Custom deserializer only for some fields with json.NET to custom-deserialize the ArrayList property while populating the remainder with default deserialization:

public class MyTestClassConverter : JsonConverter
{
    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var root = existingValue as MyTestClass ?? (MyTestClass)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        var jsonObject = JObject.Load(reader);

        var jsonItems = jsonObject["Items"].RemoveFromLowestPossibleParent();
        if (jsonItems != null && jsonItems.Type != JTokenType.Null)
        {
            root.Items.AddRange(jsonItems.ToObject<List<string []>>());
        }
        // Populate the remaining standard properties
        using (var subReader = jsonObject.CreateReader())
        {
            serializer.Populate(subReader, root);
        }
        return root;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(MyTestClass).IsAssignableFrom(objectType);
    }
}

public static class JsonExtensions
{
    public static JToken RemoveFromLowestPossibleParent(this JToken node)
    {
        if (node == null)
            return null;
        var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
        if (contained != null)
            contained.Remove();
        // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
        if (node.Parent is JProperty)
            ((JProperty)node.Parent).Value = null;
        return node;
    }
}

Then use it as follows:

var tempMyObject = JsonConvert.DeserializeObject<MyTestClass>(jsonResult,
                                                              new JsonSerializerSettings { Converters = { new MyTestClassConverter() } });

Sample fiddle #3.

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thanks for the reply. This answers my question. However , i forgot to mention something in my question. I cannot change class definition, the class is implemented in an assembly that I cannot edit class implementation therefore i cannot add any attribute on property. In this case, how can i apply your solution to my problem? – naltun Jan 11 '17 at 09:14
  • @BorisZolotov - is this the only `ArrayList` to be deserialized, or are there others in the object graph? – dbc Jan 11 '17 at 09:28
  • Yes , there are other properties in object. I need to serialize/deserialize entire object. One of the property of the object is type of `ArrayList` , and i am having problem with `ArrayList` only. – naltun Jan 11 '17 at 09:46
  • @BorisZolotov - no, I meant is this the only instance of an `ArrayList` in your object graph? Or might there be an `ArrayList` of, say, integers elsewhere? – dbc Jan 11 '17 at 09:51
  • The `ArrayList` that contains only objects type of string arrays. – naltun Jan 11 '17 at 09:55
1

Try this I hope this code help you

using List and JSON.NET

List<string[]> list = new List<string[]>();

list.Add(new string[] { "Value","value" });
list.Add(new string[] { "b2", "b22" });

var ee = JsonConvert.SerializeObject(list);

Console.WriteLine(ee);

List<string[]> ll = JsonConvert.DeserializeObject<List<string[]>>(ee);


foreach (var Valus in ll)
{
    foreach (var val in Valus)
    {
         Console.WriteLine(val);
    }
}

using Array List

string[][] strarry = { new string[] { "f1", "f2", "f3" }, new string[] { "s1", "s2", "s3" }, new string[] { "t1", "t2", "t3" } };

string SerializeArray = json.Serialize(strarry);

string[][] DeSerializeArrayList = json.Deserialize<string[][]>(SerializeArray);

foreach (var item in DeSerializeArrayList)
{
      foreach (var Subitem in item)
      {
              Response.Write(Subitem + "<br/>");
      } 
}

using List

List<Data> list = new List<Data>();

list.Add(new Data() { ID = 1, Name = "val1" });
list.Add(new Data() { ID = 2, Name = "val2" });
JavaScriptSerializer json = new JavaScriptSerializer();

string Serialize = json.Serialize(list);
Response.Write(Serialize);

List<Data> DeSerialize = json.Deserialize<List<Data>>(Serialize);

foreach (var Data in DeSerialize)
{
    Response.Write(Data.Name);
}

Data Class

public class Data
{
    public int ID { get; set; }
    public string Name { get; set; }
}
  • I was looking for a solution by using NewtonSoft's JSON.NET API with System.Collections.ArrayList. – naltun Jan 10 '17 at 15:48
  • ArrayList is not have generic type but list is a generic type if you want to Deserialize json with ArrayList of string array you get error but of you use List of array of string code successfully run without getting error.so if you want this using List and NewtonSoft's JSON.NET API Tell me I Have code. – Muhammad Usama Alam Jan 10 '17 at 16:40