0

Hello i am trying to create a hierarchy of classes using a discriminated unionand it seems i can't serialize them.I keep getting this error :

Newtonsoft.Json.JsonSerializationException: 'Self referencing loop detected for property 'AsRun' with type 'MsgPattern.Message+Run'. Path ''.'

Base class

[Serializable]
    public abstract partial class Message {
        public enum Type {
            WALK = 0,
            RUN = 1
        }
        protected abstract Type Discriminator { get; }
        public Type Kind => this.Discriminator;

        internal static Message Create(string data) {
            var message = JsonConvert.DeserializeObject<Message>(data);
            switch (message.Kind) {
                case Type.RUN:message= message.AsRun;break;
                case Type.WALK:message= message.AsWalk;break;
            }
            return message;
        }
        [JsonIgnore]
        public bool IsWalk => this.Kind==Type.Walk;
        [JsonIgnore]
        public bool IsRun => this.Kind==Type.Run;
        [JsonIgnore]
        public Message.Walk AsWalk => this as Message.Walk;
        [JsonIgnore]
        public Message.Run AsRun => this as Message.Run;


    }

Dervived

partial class Message {

        public class Run : Message {
            protected override Type Discriminator => Type.RUN;
            public string Location { get; set; }
            public int Speed { get; set; }
        }
    }

partial class Message {

        public class Walk : Message {
            protected override Type Discriminator => Type.WALK;
            public int Gait { get; set; }
            public bool IsJogging { get; set; }
        }
    }

Usage

class Program {


   static void Main(string[] args) {
        Message.Run run = new Message.Run { Location = "asa", Speed = 33 };
        string data = JsonConvert.SerializeObject(run);
        Message msg=Message.Create(data);
    }
}

I will get these type of messages via json and i want to be able to do actions based on their type. I do not understand why i can't serialize them .

P.S I know it's a self-referencing loop but I need those As[something] and Is[Something] fields.

Bercovici Adrian
  • 6,415
  • 9
  • 47
  • 90
  • 1
    C# doesn't have discriminated unions. You can *emulate* them in a way, by inheriting from a common interface and using pattern matching. In any case, JSON serialization isn't part of the DU concept and shouldn't be mixed into them. That's what causes the problem here - an abstract class was used to add serialization. The abstract class is full of serialization *properties*. Any serializer will try to serialize them as well, even though they have nothing to do with the DU types themselves. The properties end up returning the object itself, resulting in a self referencing loop – Panagiotis Kanavos Feb 15 '19 at 09:14
  • Yes but i need those methods at `deserialization`. – Bercovici Adrian Feb 15 '19 at 09:15
  • 1
    At the very least, `AsRun` and company should be methods. They aren't *properties* of the classes. They aren't needed at all anyway, pattern matching syntax like `if (x is Walk w)` or `switch x: case Walk w:` takes care of that, the same way it does in F@. Serialization should be moved out of the abstract class. – Panagiotis Kanavos Feb 15 '19 at 09:16
  • You are right and i have add `[JsonIgnore]` on them , i updated my post.But still now it throws when trying to `deserialize` an abstract class. – Bercovici Adrian Feb 15 '19 at 09:19
  • 1
    You *don't* any of those properties for deserialization. The deserializer will create the appropriate type based on the serialized data. You need extra data only if you want to deserialize data when there's no type data and you don't know the type in advance. That's a general problem with inheritance, not specific to DUs. Storing a type identifier is just one of the ways this can be done – Panagiotis Kanavos Feb 15 '19 at 09:20
  • Well i have stored but already the `identifier` but that would mean that i can't `deserialize` from the get go.I would need to read some `bytes` from the `input string` get the `id` and then deserialize the rest of the `string ` accordingly , am i right? – Bercovici Adrian Feb 15 '19 at 09:22
  • `AsRun` is a *method*, not a property. It's not part of the contract of the the Walk or Run messages or their state. It does something with them, which means it's a function. As for trying to deserialize an abstract method, you can't, unless the data contains type information recognized by the serializer itself – Panagiotis Kanavos Feb 15 '19 at 09:23
  • The code you posted looks like the code generated by F# for discriminated unions, but not all of it. *JSON serialization* of polymorphic classes has nothing to do with that though. That's a different question, already handled by JSON.NET in its own way. There are many duplicate questions already, eg [this one](https://stackoverflow.com/questions/19307752/deserializing-polymorphic-json-classes-without-type-information-using-json-net) or [this one](https://stackoverflow.com/questions/20995865/deserializing-json-to-abstract-class). You can create a custom JsonConverter or emit type information – Panagiotis Kanavos Feb 15 '19 at 09:28

0 Answers0