从 ObjectInputStream 反序列化为子类之一

Deserialize from ObjectInputStream to one of the subclasses

我有一个名为 Message 的摘要 class:

public abstract class Message
{
    public enum MsgType
    {
        TypeA, TypeB
    }

    public String Sender;
    public MsgType Type;
}

其中有两个子classes:

public class MessageA extends Message
{
    public String A;

    public MessageA()
    {
        Type = MsgType.TypeA;
    }
}

public class MessageB extends Message
{
    public int B;

    public MessageB()
    {
        Type = MsgType.TypeB;
    }
}

我想通过实例化 MessageAMessageB 使用 Simple 库直接从 ObjectsInputStream 反序列化对象,具体取决于从流中读取的对象。

我试过这样做:

inputStream = new ObjectInputStream(clientSocket.getInputStream());
Message msg = serializer.read(Message.class, inputStream);
// check whether it's MessageA or MessageB by checking Type

但它不起作用,因为编译器无法将 MessageAMessageB 反序列化为 Message class。另外,我很确定我会在这个过程中丢失 subclass' 字段。

有什么正确的方法吗?

public class Main {
    public static void main( String[] args ) throws Exception {
        Serializer serializer = new Persister();
        MessageB messageB = new MessageB( "example", 123 );
        File result = new File( "example.xml" );
        serializer.write( messageB, result );
        MessageB msgB = serializer.read( MessageB.class, result );
        System.out.println( msgB );
        try {
            if( serializer.validate( MessageA.class, result ) ){
                System.out.println( "valid for MessageA" );
            }
        } catch( Exception e ){
            System.out.println( "NOT valid for MessageA" );
        }
        try {
            if( serializer.validate( MessageB.class, result ) ){
                System.out.println( "valid for MessageB" );
            }
        } catch( Exception e ){
            System.out.println( "NOT valid for MessageB" );
        }
    }
}

public abstract class Message{
    public enum MsgType {
        TypeA, TypeB
   }
    @Element
    private String sender;
   @Element
    private MsgType type;
    protected Message(){
    }
    protected Message( String s, MsgType t ){
        sender = s;
        type = t;
    }
    public String getSender(){ return sender; }
    public MsgType getType(){ return type; }
    public String toString(){
        return "type=" + type + ", sender=" + sender;
    }
}

@Root
public class MessageA extends Message {
    @Element
    private String a;
    public MessageA(){
    }
    public MessageA( String s, String a ){
        super( s, MsgType.TypeA );
        this.a = a;
    }
    public String getA(){ return a; }
    public String toString(){
        return super.toString() + ", a=" + a;
    }
}
@Root
public class MessageB extends Message {
    @Element
    private int b;
    public MessageB(){
    }
    public MessageB( String s, int b ){
        super( s, MsgType.TypeA );
        this.b = b;
    }
    public int getB(){ return b; }
    public String toString(){
        return super.toString() + ", b=" + b;
    }
}

这个效果很好。

type=TypeA, sender=example, b=123
NOT valid for MessageA
valid for MessageB

XML文件写成:

<messageB>
  <sender>example</sender>
  <type>TypeA</type>
  <b>123</b>
</messageB>

为每个子类添加一个@Root(name = "...") 注释

例如:

public interface StringOperation {
    String performOperation(String input);
}

@Root(name = "upperCaseStringOperation")
public class UpperCaseStringOperation implements StringOperation {
    public UpperCaseStringOperation() {
    }

    @Override
    public String performOperation(String input) {
        return (input == null) ? (null) : (input.toUpperCase());
    }
}

@Root(name = "lowerCaseStringOperation")
public class LowerCaseStringOperation implements StringOperation {
    public LowerCaseStringOperation() {
    }

    @Override
    public String performOperation(String input) {
        return (input == null) ? (null) : (input.toLowerCase());
    }
}

@Root(name = "noOpStringOperation")
public class NoOpStringOperation implements StringOperation {
    public NoOpStringOperation() {
    }

    @Override
    public String performOperation(String input) {
        return input;
    }
}

@Root
public class Data {

    @Element
    private String name;

    @Element
    private StringOperation nameOperation;

    @Element
    private String email;

    @Element
    private StringOperation emailOperation;

    public Data() {
        nameOperation = new NoOpStringOperation();
        emailOperation = new NoOpStringOperation();
    }

    public String getName() {
        return name;
    }

    public String getProcessedName() {
        return nameOperation.performOperation(getName());
    }

    public String getEmail() {
        return email;
    }

    public String getProcessedEmail() {
        return emailOperation.performOperation(getEmail());
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setNameOperation(StringOperation operation) {
        this.nameOperation = operation;
    }

    public void setEmailOperation(StringOperation operation) {
        this.emailOperation = operation;
    }

    @Override
    public String toString() {
        return String.format("Data(name = %s, processedName = %s, email = %s, processedEmail = %s)",
                             getName(),
                             getProcessedName(),
                             getEmail(),
                             getProcessedEmail());
    }
}

示例代码:

        Data before = new Data();
        before.setName("Joseph Wallflower");
        before.setNameOperation(new UpperCaseStringOperation());
        before.setEmail("Joseph.Wallflower@SomeCompany.com");
        before.setEmailOperation(new LowerCaseStringOperation());

        System.out.format("BEFORE: %s%n", before.toString());

        Serializer serializer = new Persister();
        File file = new File(System.getenv("USERPROFILE")+File.separator+"Documents"+File.separator+"simple.xml");
        serializer.write(before, file);

结果:

<data>
   <name>Joseph Wallflower</name>
   <nameOperation class="simplexml.UpperCaseStringOperation"/>
   <email>Joseph.Wallflower@SomeCompany.com</email>
   <emailOperation class="simplexml.LowerCaseStringOperation"/>
</data>