代码之家  ›  专栏  ›  技术社区  ›  Natrium

抽象类的序列化

  •  12
  • Natrium  · 技术社区  · 15 年前

    我正在尝试序列化,而Abstact类正面临一个问题。

    我在谷歌上搜索答案,发现 this blogitem . 我试过了,也试过了。

    好的,非常好。但请查看对该项目的评论:

    这种方法似乎隐藏了 真正的问题是 OO设计实现不准确 图案,即工厂图案。

    必须将基类更改为 引用任何新的工厂类 自我挫败。

    经过深思熟虑,代码 可以更改为 类型可以与 抽象类(通过 接口),没有xmlcinclude 必修的。

    我建议进一步研究 工厂模式 你想在这里实现什么。

    评论员在说什么?他有点含糊不清。有人能详细解释一下吗(举个例子)?或者他只是胡说八道?

    更新(阅读第一个答案后)

    评论员为什么要谈论

    工厂模式

    代码可以更改为 派生类型不能与 抽象类(通过 界面的奇迹)

    ?

    他想做一个这样的界面吗?

    public interface IWorkaround
    {
        void Method();
    }
    
    public class SomeBase : IWorkaround
    {
        public void Method()
        {
            // some logic here
        }
    }
    
    public class SomeConcrete : SomeBase, IWorkaround
    {
        public new void Method()
        {
            base.Method();
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  39
  •   Marc Gravell    15 年前

    他是对的,也是错的。

    像这样的事情 BinaryFormatter ,这不是问题;序列化流包含完整类型的元数据,因此如果您有:

    [Serializable] abstract class SomeBase {}
    [Serializable] class SomeConcrete : SomeBase {}
    ...
    SomeBase obj = new SomeConcrete();
    

    序列化 obj 包括“我是一个 SomeConcrete “在小溪里。这使生活变得简单,但很冗长,尤其是在重复的时候。它也很脆弱,因为它在反序列化时需要相同的实现;对于不同的客户机/服务器实现或长期存储都不好。

    XmlSerializer (我想是博客所说的),没有元数据——只有元素名(或者 xsi:type 属性)用于帮助识别使用的对象。为了使其工作,序列化程序 需要提前知道 什么名字对应什么类型。

    最简单的方法是用我们知道的子类修饰基类。然后,序列化程序可以检查其中的每一个(以及任何其他特定于XML的属性),以确定当它看到 <someConcreteType> 元素,映射到 局部混凝土 实例(请注意,名称不需要匹配,因此它不能只按名称查找)。

    [XmlInclude(typeof(SomeConcrete))]
    public abstract class SomeBase {}
    public class SomeConcrete : SomeBase {}
    ...
    SomeBase obj = new SomeConcrete();
    XmlSerializer ser = new XmlSerializer(typeof(SomeBase));
    ser.Serialize(Console.Out, obj);
    

    但是,如果他是一个纯粹主义者(或者数据不可用),那么有一个替代方法;您可以指定所有这些数据 分别地 通过重载的构造函数 XML序列化程序 . 例如,您可以从配置中查找已知子类型集(或IOC容器),然后手动设置构造函数。这不是很棘手,但很棘手,除非你 实际上需要它 .

    public abstract class SomeBase { } // no [XmlInclude]
    public class SomeConcrete : SomeBase { }
    ...
    SomeBase obj = new SomeConcrete();
    Type[] extras = {typeof(SomeConcrete)}; // from config
    XmlSerializer ser = new XmlSerializer(typeof(SomeBase), extras);
    ser.Serialize(Console.Out, obj);
    

    此外,还有 XML序列化程序 如果使用自定义ctor路由,则缓存并重新使用 XML序列化程序 实例;否则,每次使用都会加载一个新的动态程序集-非常昂贵(无法卸载它们)。如果使用简单的构造函数,它将缓存并重新使用模型,因此只使用单个模型。

    Yagni规定我们应该选择最简单的选项;使用 [XmlInclude] 消除了对复杂构造函数的需要,并消除了对缓存序列化程序的担心。另一种选择是存在的,并且得到了完全支持。


    重新回答您的后续问题:

    根据“工厂模式”,他说的是你的代码 不知道 局部混凝土 可能是由于IOC/DI或类似的框架;所以您可能有:

    SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);
    

    哪一个合适 SomeBase 具体实现,实例化并返回。显然,如果我们的代码不知道具体类型(因为它们只在配置文件中指定),那么我们就不能使用 XmlInclude 但是我们 可以 分析配置数据并使用ctor方法(如上所述)。实际上,大多数时候 XML序列化程序 与POCO/DTO实体一起使用,因此这是一个人为问题。

    重新接口;同样的事情,但更灵活(接口不需要类型层次结构)。但是 XML序列化程序 不支持此模型。坦率地说,这不是它的工作。它的工作是允许您存储和传输数据。不是实现。任何XML架构生成的实体都不会 方法。数据是具体的,而不是抽象的。只要您认为“DTO”,接口辩论就不是问题。对于无法在边界上使用界面感到烦恼的人,他们没有接受关注点分离,即他们试图做到:

    Client runtime entities <---transport---> Server runtime entities
    

    而不是限制性较小的

    Client runtime entities <---> Client DTO <--- transport--->
               Server DTO <---> Server runtime entities
    

    现在,在很多(大多数?)案例DTO和实体 可以 保持不变;但是如果您试图做传输不喜欢的事情,请引入DTO;不要与序列化程序作斗争。同样的逻辑也适用于人们在努力书写他们的对象时:

    class Person {
        public string AddressLine1 {get;set;}
        public string AddressLine2 {get;set;}
    }
    

    作为表单的XML:

    <person>
        <address line1="..." line2="..."/>
    </person>
    

    如果需要,请生成与传输相对应的DTO,并在实体和DTO之间映射:

    // (in a different namespace for the DTO stuff)
    [XmlType("person"), XmlRoot("person")]
    public class Person {
        [XmlElement("address")]
        public Address Address {get;set;}
    }
    public class Address {
        [XmlAttribute("line1")] public string Line1 {get;set;}
        [XmlAttribute("line2")] public string Line2 {get;set;}
    }
    

    这也适用于所有其他的麻烦,比如:

    • 为什么我需要一个无参数的构造函数?
    • 为什么我需要集合属性的setter?
    • 为什么不能使用不可变类型?
    • 为什么我的类型必须公开?
    • 如何处理复杂的版本控制?
    • 如何处理具有不同数据布局的不同客户机?
    • 为什么我不能使用接口?

    你并不总是有这些问题,但是如果你有——介绍一个DTO(或几个),你的问题就消失了。回到关于接口的问题;DTO类型可能不是基于接口的,但您的运行时/业务类型可以是。

        2
  •  0
  •   Joao Pedro Leite S Lisboa    6 年前
    **Example of Enum Abstract Serializer
    
    Simple example of an abstract enum ...(Java)(Spring-Boot) 
    ----------------------------------------------------------------------------------**
    
    
    @JsonSerialize(using = CatAbstractSerializer.class)
    public enum CatTest implements Tes{
    
        TYPE1(1, "Type 1"), TYPE2(2, "Type 2");
    
        private int id;
        private String nome;
    
        private CatTest(int id, String nome) {
            // TODO Auto-generated constructor stub
    
            this.id = id;
            this.nome = nome;
    
    
        }
        @JsonValue
        public int getId() {
            return id;
        }
        @JsonSetter
        public void setId(int id) {
            this.id = id;
        }
        @JsonValue
        public String getNome() {
            return nome;
        }
        @JsonSetter
        public void setNome(String nome) {
            this.nome = nome;
        }
        @Override
         @JsonValue
         public String toString() {
                return nome;
         }
         @JsonCreator
            public static CatTest fromValueString(String nome) {
                if(nome == null) {
                    throw new IllegalArgumentException();
                }
                for(CatTest nomeSalvo : values()) {
                    if(nome.equals(nomeSalvo.getNome())) {
                        return nomeSalvo;
                    }
                }
                throw new IllegalArgumentException();
            }
    
    
    }
    
    
    public interface Tes {
    
        @JsonValue
        int getId();
    
        @JsonValue
        String getNome();
    
        @JsonSetter
        void setId(int id);
    
        @JsonSetter
        void setNome(String nome);
    
    
    }
    
    public class CatAbstractSerializer<T extends Tes> extends JsonSerializer<T> {
    
        @Override
        public void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException, JsonProcessingException {
            // TODO Auto-generated method stub
    
            gen.writeStartObject();
            gen.writeFieldName("id");
            gen.writeNumber(value.getId());
            gen.writeFieldName("name");
            gen.writeString(value.getNome());
            gen.writeEndObject();
    
        }
    
    }