我正在尝试序列化,我面临一个抽象类的问题。
我用Google搜索了一个答案,然后我找到了this blogitem。 我尝试过这项工作。
好的,非常好。但请查看该项目的评论:
这种方法似乎隐藏着 真正的问题,那是一个 OO设计的实施不准确 模式,即工厂模式。
必须将基类更改为 引用任何新的工厂类 弄巧成拙。
经过一番思考,代码 可以更改为任何派生的地方 类型可以与 抽象类(通过奇迹) 接口),没有XmlInclude 必需的。
我建议进一步研究 似乎是工厂模式 你想在这里实施什么。
评论员在谈论什么?他有点模糊。有人可以更详细地解释它(举个例子)吗?或者他只是胡说八道?
更新(阅读第一个回答后)
为什么评论员会谈论
工厂模式
和
代码可以更改为任何位置 派生类型可以与 抽象类(通过 接口的奇迹)
他想制作一个这样的界面吗?
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();
}
}
答案 0 :(得分:40)
他同时也是对的。
像BinaryFormatter
这样的事情,这不是问题;序列化流包含完整类型元数据,因此如果您有:
[Serializable] abstract class SomeBase {}
[Serializable] class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
并序列化obj
,然后它在流中包含“我是SomeConcrete
”。这使得生活变得简单,但是很冗长,特别是在重复时。它也很脆弱,因为它在反序列化时需要相同的实现;对于不同的客户端/服务器实现或长期存储都不利。
使用XmlSerializer
(我猜博客正在谈论),没有元数据 - 但元素名称(或xsi:type
属性)用于帮助识别使用的内容。为了实现这一点,序列化程序需要提前知道哪些名称映射到哪些类型。
最简单的方法是使用我们所知的子类来装饰基类。然后,序列化程序可以检查这些(以及任何其他特定于xml的属性)中的每一个,以确定当它看到<someConcreteType>
元素时,映射到SomeConcrete
实例(请注意,这些名称不会需要匹配,所以它不能只是通过名字寻找它。
[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);
然而,如果他是一个纯粹主义者(或数据不可用),那么还有另一种选择;您可以通过重载的构造函数将所有这些数据单独指定 XmlSerializer
。例如,您可以从配置(或可能是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);
此外,如果您使用自定义ctor路由XmlSerializer
,则缓存并重新使用XmlSerializer
实例非常重要;否则每次使用都会加载一个新的动态组件 - 非常昂贵(它们无法卸载)。如果使用简单构造函数,它会缓存并重新使用模型,因此只使用单个模型。
YAGNI要求我们选择最简单的选择;使用[XmlInclude]
消除了对复杂构造函数的需求,并且无需担心缓存序列化程序。另一个选项就在那里,但完全支持。
重新提出你的后续问题:
通过“工厂模式”,他正在谈论你的代码不知道 SomeConcrete
的情况,可能是由于IoC / DI或类似的框架;所以你可能有:
SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);
找出适当的SomeBase
具体实现,实例化并交回来。显然,如果我们的代码不知道具体类型(因为它们只在配置文件中指定),那么我们就不能使用XmlInclude
;但是我们可以解析配置数据并使用ctor方法(如上所述)。实际上,大多数时候XmlSerializer
与POCO / DTO实体一起使用,所以这是一个人为关注的问题。
重新接口;同样的事情,但更灵活(接口不要求类型层次结构)。但XmlSerializer
不支持此模型。坦率地说,很难;那不是它的工作。它的工作是允许您存储和传输数据。没有实施。任何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;}
}
这也适用于所有其他琐事:
你并不总是有这些问题;但如果你这样做 - 介绍一个DTO(或几个),你的问题就会消失。回过头来讨论接口问题; DTO类型可能不是基于接口的,但您的运行时/业务类型可以是。
答案 1 :(得分:0)
**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();
}
}