Как десериализовать вложенные многоуровневые полиморфные типы - PullRequest
0 голосов
/ 24 февраля 2019

Здравствуйте, у меня есть иерархия классов, где на каждом уровне есть поле payload и поле id, с помощью которого вы можете десериализовать полезную нагрузку. Мой вопрос: как вы можете десериализовать что-то подобное, учитывая, что я этого не делаю?хочу сгладить иерархию.

Мне нужно принимать решения на каждом шагу:

        public class Root
        {
            public int id { get; set; }
            public Message msg { get; set; }
        }
        public abstract class Message
        {
            public int MessageID { get; set; }
        }

        public abstract class Com : Message
        {
            public int ComMsgId { get; set; }
        }
        public abstract class Game : Message
        {
            public int GameMsgID { get; set; }
        }
        public class Game1 : Game
        {
            public string Data { get; set; }
        }
        public class Com1 : Com
        {
            public bool IsDone { get; set; }
        }

Я приложил картинку для дальнейшего уточнения:
Hierarchy of classes

PS Я пытался использовать библиотеку JsonSubTypes, но она не работает на многоуровневом полиморфизме. Есть ли что-нибудь еще, что я могу сделать?

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Я решил это, используя поле abstract в качестве discriminator в атрибуте. Это поле будет определено в root и будет переопределено только в leaf -s.Затем отдельно я бы реализовал различающееся объединение.

Чтобы иметь возможность разыгрывать на любом уровне в иерархии, я бы украсил промежуточные абстрактные классы их соответствующими листами:

Так, например, имея классиерархия, подобная этой:

M
M1:M, M2:M
[M11:M1, M12:M1];[M21:M2, M22:M2]
где листы M11,M12,M21,M22 я написал следующий код:

Root

[JsonConverter(typeof(JsonSubTypes.JsonSubtypes), propType)]
[JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M1.M11),Leaf.M11)]
[JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M1.M12), Leaf.M12)]
[JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M2.M21), Leaf.M21)]
[JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M2.M22), Leaf.M22)]
public abstract partial class M
{
    public static void Process(M message){

        switch(message.MKind){
            case M.Discriminator.M1 : M.M1.Process(message.AsM1);break;
            case M.Discriminator.M2: M.M2.Process(message.AsM2);break;
        }
    }
    [JsonIgnore]
    public bool IsM1=>this.MKind==Discriminator.M1;
    [JsonIgnore]
    public bool IsM2=>this.MKind==Discriminator.M2;
    [JsonIgnore]
    public M1 AsM1=>this as M1;
    [JsonIgnore]
    public M2 AsM2=>this as M2;
    private const string propType="$LeafKind";

    public enum Discriminator {
        M1=0,
        M2=1
    }
    public enum Leaf {
        M11=M1.Discriminator.M11*10,
        M12=M1.Discriminator.M12*10,
        M21=M2.Discriminator.M21*10,
        M22=M2.Discriminator.M22*10
    }



    [JsonProperty(propType)]
    public abstract Leaf LeafKind{get;}



    protected abstract Discriminator MKind{get;}
    public Discriminator Kind=>this.MKind;

    public DateTime timestamp { get;}
}

Абстрактная ветвь (уровень 2)

       [JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M1.M11), 
       M.Leaf.M11)]
       [JsonSubTypes.JsonSubtypes.KnownSubType(typeof(M.M1.M12),M.Leaf.M12)]
        public abstract partial class M1:M
        {
            public static   void  Process(M1 message){
                switch(message.M1Kind){
                    case M1.Discriminator.M11 : M1.M11.Process(message.AsM11);break;
                    case M1.Discriminator.M12:  M1.M12.Process(message.AsM12);break;
                }
            }
            protected override M.Discriminator MKind => M.Discriminator.M1;
            public new enum Discriminator {
                M11=10,
                M12=11
            }
            public new Discriminator Kind => this.M1Kind;

            protected abstract Discriminator M1Kind { get; }
            [JsonIgnore]
            public bool IsM11=>this.M1Kind==M1.Discriminator.M11;
            [JsonIgnore]
            public bool IsM12=>this.M1Kind==M1.Discriminator.M12;
            [JsonIgnore]
            public M11 AsM11=>this as M11;
            [JsonIgnore]
            public M12 AsM12=>this as M12;   
        }

Лист

public class M11 : M1
            {
                public static void Process(M11 message){
                    Console.WriteLine(message.Value);
                }

                public bool Value{get;set;}


                protected override Discriminator M1Kind => Discriminator.M11;

                public override Leaf LeafKind => Leaf.M11;
            }

Для возможности приведения объекта типа M11 в M1 Мне нужно украсить M1 с помощью KnownSubTypes, но переопределить discriminator только в листьях.

PS Я не добавил сюда все subtypes, кроме васполную реализацию с небольшим образцом можно найти в исходном коде

обновлении Я переопределил иерархию, используя enum вместо строки как Discriminator.

0 голосов
/ 25 февраля 2019

Причина, по которой нет более чистого подхода к этому, заключается в том, что JSON не содержит метаданных, а вывод подклассов на основе имен свойств является очень сложным и ресурсоемким подходом.

Я решаю эту проблемусоздав виртуальное свойство в классе Message, которое указывает тип десериализуемого класса и присваивает ему значение в каждом подклассе.Например, в Com1

public override string ClassName => nameof(Com1)

Затем реализуйте CustomSubClassConverter, унаследованный JsonConverter, в котором вы переопределяете атрибут ReadJson для преобразования JSON, применяя регистр переключателя к свойству ClassName.

Тогдапометьте свой Message класс [JsonConverter(typeof(CustomSubClassConverter))]

Я буду более чем рад предоставить вам код CustomSubClassConverter, если вы хотите, чтобы я.

ОБНОВЛЕНИЕ

Вы также можете использовать отражение, чтобы получить подкласс, вместо использования switch / case, если вы хотите, чтобы его было легче адаптировать к добавлению новых классов

...