1

I have a set of classes like the following:

public class Class1 : IClass
{
    public List<Class2> Class2List { get { return Class3Instance.Class2List; } }

    public Class3 Class3Instance { get; set; }
}

public class Class2
{
    public string Field1 { get; set; }
    public int Field2 { get; set;}
}

public class Class3
{
    public List<Class2> Class2List { get; set; }
}

I am storing information about a list of Class1 and the serialization of said list works as expected i.e. the resulting file contains all the information I serialized it with.

When I deserialize though, there's a problem although no exceptions are given and I can read information, there's extra information being added to Class3.

Class3 is a class which is used in my application as a list of Class3 instances for its lifetime, I also use it to query information about other fields as needed in the application flow that are not relevant to this issue.

The thing is, when I read a list of Class1 with 1 or more items in Class2List, I see that those items are being added to the Class2List property of the Class3 that is related to Class1.

If more than 1 element in the list of Class1 references the same Class3 instance, all of the Class2List members are added to the referenced Class3.

I do not know why this is happening of what I am doing wrong for this behaviour to happen, but I've found that marking the field Class2List as JsonIgnore solves this problem and I see no instances of Class1.Class2List being added to Class3.Class2List.

Could anyone explain to me why the behaviour I've described actually happens? I am lost as to why it does.

To Provide an example, serializing with the last version of Newtonsoft.Json the result of the following code:

Class3 class3 = new Class3();
class3.Class2List2 = new List<Class2>();
class3.Class2List2.Add(new Class2() { Field1 = "Member1", Field2 = 1 });
class3.Class2List2.Add(new Class2() { Field1 = "Member2", Field2 = 2 });
class3.Class2List2.Add(new Class2() { Field1 = "Member3", Field2 = 3 });
class3.Class2List2.Add(new Class2() { Field1 = "Member4", Field2 = 4 });
class3.Class2List2.Add(new Class2() { Field1 = "Member5", Field2 = 5 });

Class1 class1 = new Class1();
class1.Class3Instance = class3;

Gives this Json:

{
  "Class3Instance": {
    "Class2List2": [
      {
        "Field1": "Member1",
        "Field2": 1
      },
      {
        "Field1": "Member2",
        "Field2": 2
      },
      {
        "Field1": "Member3",
        "Field2": 3
      },
      {
        "Field1": "Member4",
        "Field2": 4
      },
      {
        "Field1": "Member5",
        "Field2": 5
      }
    ]
  },
  "Class2List": [
    {
      "Field1": "Member1",
      "Field2": 1
    },
    {
      "Field1": "Member2",
      "Field2": 2
    },
    {
      "Field1": "Member3",
      "Field2": 3
    },
    {
      "Field1": "Member4",
      "Field2": 4
    },
    {
      "Field1": "Member5",
      "Field2": 5
    }
  ]
}

Then, if I inmediately deserialize the JSON and serialize it again, I get the following result:

{
  "Class3Instance": {
    "Class2List2": [
      {
        "Field1": "Member1",
        "Field2": 1
      },
      {
        "Field1": "Member2",
        "Field2": 2
      },
      {
        "Field1": "Member3",
        "Field2": 3
      },
      {
        "Field1": "Member4",
        "Field2": 4
      },
      {
        "Field1": "Member5",
        "Field2": 5
      },
      {
        "Field1": "Member1",
        "Field2": 1
      },
      {
        "Field1": "Member2",
        "Field2": 2
      },
      {
        "Field1": "Member3",
        "Field2": 3
      },
      {
        "Field1": "Member4",
        "Field2": 4
      },
      {
        "Field1": "Member5",
        "Field2": 5
      }
    ]
  },
  "Class2List": [
    {
      "Field1": "Member1",
      "Field2": 1
    },
    {
      "Field1": "Member2",
      "Field2": 2
    },
    {
      "Field1": "Member3",
      "Field2": 3
    },
    {
      "Field1": "Member4",
      "Field2": 4
    },
    {
      "Field1": "Member5",
      "Field2": 5
    },
    {
      "Field1": "Member1",
      "Field2": 1
    },
    {
      "Field1": "Member2",
      "Field2": 2
    },
    {
      "Field1": "Member3",
      "Field2": 3
    },
    {
      "Field1": "Member4",
      "Field2": 4
    },
    {
      "Field1": "Member5",
      "Field2": 5
    }
  ]
}

As you can see, the Class2List members have been duplicated when I deserialized the JSON.

Oscar Guillamon
  • 368
  • 3
  • 13
  • Class2 code is missing – Marco Salerno Apr 23 '19 at 07:21
  • It is not relevant but I'll add it as well, thanks for the heads up – Oscar Guillamon Apr 23 '19 at 07:22
  • 1
    Can you post a [MCVE]? – aloisdg Apr 23 '19 at 07:26
  • @aloisdg I tried to reduce the class structure from my application to only what is relevant to the problem, for an example like the one you are showing me, should I provide a JSON which reproduces the problem or a solution that reproduces the problem as expected? – Oscar Guillamon Apr 23 '19 at 07:28
  • 1
    @OscarGuillamon: why not run your reduced example to create serialization output then deserialize that output. You could include those results in the question and tell us how that output is faulty. As of now anyone who want to answer your question would have to create such a program himself, which would take a bit too much time for just for the answer. – mortb Apr 23 '19 at 07:34
  • @mortb Ok, I'll do that, thank you for helping to provide a sufficiently good question to ease answering – Oscar Guillamon Apr 23 '19 at 07:35
  • No problem, it's just that all references to "Class1, Class3 which is a list of Class2 that is also a property" makes my head dizzy when trying to read it... ;) – mortb Apr 23 '19 at 07:36
  • @mortb is this enough of an example to meet community guidelines? – Oscar Guillamon Apr 23 '19 at 08:11
  • @OscarGuillamon: much better :) – mortb Apr 23 '19 at 08:12
  • Do you really need both properties? `Class2List` is just a "shortcut" to `Class3Instance.Class2List`. The deserialization can't really work out that they are the same and that items should just be added to one of them. If you need a solution you could use a contract resolver https://stackoverflow.com/questions/25749509/how-can-i-tell-json-net-to-ignore-properties-in-a-3rd-party-object – mortb Apr 23 '19 at 08:25
  • I don't really need both, this is code from a collaborative project I am working on and was told to fix this behaviour. As I've said, ignoring the property solves this problem, I was just wondering what is the reason for this to happen. – Oscar Guillamon Apr 23 '19 at 08:30
  • @mortb Do you believe this is community wiki material? – Oscar Guillamon Apr 23 '19 at 08:42
  • I would say the best solution is to remove one of the redundant properties if you are free to do so. Less verbose code is nearly always a better solution. From what I've read about the `JsonIgnore` attribute it will only affect serialization and not deserialization, so please note that if you use attribute – mortb Apr 23 '19 at 10:45
  • It is strange, since if I set the attribute JsonIgnore to the shortcut property, the behaviour comes as expected. – Oscar Guillamon Apr 23 '19 at 12:19
  • Although I'll ask the person in charge of this code if it is safe to remove the shortcut property . – Oscar Guillamon Apr 23 '19 at 12:21

1 Answers1

2

As @mortb says:

The problem lays in the shortcut to Class3Instance.Class2List, any members of said List that have been serialized in the JSON will be added to the reference of the shortcut, in addition to the members said reference previously had in the same JSON file.

This happens because the deserialization does not know how to discern between the shorcut reference Class3Instance.Class2List in Class1 and Class2List in Class3, so it does not know if it should ignore the serializing or deserializing operations on the shortcut field.

In order to fix this problem, either a contract resolver or setting the attribute [JsonIgnore] to the shortcut reference, solves this problem.

Oscar Guillamon
  • 368
  • 3
  • 13