具有基类的Jaxb不会序列化派生元素

时间:2018-10-09 09:35:05

标签: java serialization jaxb

我在树状结构中有一个模块列表。基类包含子级和一些通用属性,而派生类应提供其各个字段。现在,当我尝试使用JAXB进行序列化时,我仅获得基类的成员,而没有获得派生类的成员。

Application.java

import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Application
{
    public static void main(String[] args)
    {
        try
        {
            Emulators emulators = new Emulators();
            emulators.addChild(new VICEModule());
            emulators.addChild(new VICEModule());

            StringWriter sw = new StringWriter();
            JAXBContext jaxbContext = JAXBContext.newInstance(emulators.getClass());
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
            jaxbMarshaller.marshal(emulators, sw);
            String s = sw.toString();
            System.out.println(s);
        }
        catch(Throwable e)
        {
            System.err.println("Exception:"+e);
        }
    }
}

ModuleBase.java

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;

import com.sun.xml.internal.txw2.annotation.XmlAttribute;

public class ModuleBase
{
    private List<ModuleBase> mChilds = new ArrayList<>();
    private String mModuleId;
    private String mModuleName;

    public ModuleBase(String oModuleId, String oModuleName)
    {
        setModuleId(oModuleId);
        setModuleName(oModuleName);
    }

    public void addChild(ModuleBase oModuleNode)
    {
        mChilds.add(oModuleNode);
    }

    @XmlElement(name="ModuleList")
    public List<ModuleBase> getChildModules()
    {
        return mChilds;
    }

    @XmlAttribute
    public String getModuleId()
    {
        return mModuleId;
    }

    public void setModuleId(String oModuleId)
    {
        mModuleId = oModuleId;
    }

    @XmlAttribute
    public String getModuleName()
    {
        return mModuleName;
    }

    public void setModuleName(String oModuleName)
    {
        mModuleName = oModuleName;
    }
}

Emulators.java

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "Emulators")
public class Emulators
    extends ModuleBase 
{
    public Emulators()
    {
        super("IdEmu129872q53", "Emulators");
    }
}

VICEModule.java

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;

@XmlRootElement(name = "VICEModule")
public class VICEModule
    extends ModuleBase
{
    private String mInstallationPath;
    private int mPort;

    public VICEModule()
    {
        super("Id123456", "Vice");

        mPort = 6510;
    }

    //@XmlElement(name="InstallationPath")
    @XmlValue
    public String getInstallationPath()
    {
        return mInstallationPath;
    }

    public void setInstallationPath(String oPath)
    {
        mInstallationPath = oPath;
    }

    //@XmlElement(name="Port")
    @XmlValue
    public int getPort()
    {
        return mPort;
    }

    public void setPort(int nPort)
    {
        mPort = nPort;
    }
}

现在,当我对其进行序列化时,会得到以下XML,其中缺少VICEModule值,并且该模块被列为ModuleList而不是VICEModule,并且基字段被放置为标签而不是属性:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators>
    <ModuleList>
        <moduleId>Id123456</moduleId>
        <moduleName>Vice</moduleName>
    </ModuleList>
    <ModuleList>
        <moduleId>Id123456</moduleId>
        <moduleName>Vice</moduleName>
    </ModuleList>
    <moduleId>IdEmu129872q53</moduleId>
    <moduleName>Emulators</moduleName>
</Emulators>

所以它应该是这样的:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators ModuleId="IdEmu129872q53" ModuleName="Emulators">
    <VICEModule ModuleId="Id1" ModuleName="Name">
        <InstallationPath>Path1</InstallationPath>
        <Port>6510</Port>
    </VICEModule>
    <VICEModule ModuleId="Id2" ModuleName="Name">
        <InstallationPath>Path2</InstallationPath>
        <Port>6511</Port>
    </VICEModule>
</Emulators>

当我使用VICEModule作为基类时,XML看起来更像它(尽管仍然没有属性)。

1 个答案:

答案 0 :(得分:0)

编写这个小型演示应用程序很有帮助,因为现在我可以更轻松地进行编组工作并最终使它工作。我将更改发布到此处,以使此示例完整,以防万一有人需要它作为参考。 driver.switchTo().frame(driver.findElement(By.xpath(".//*@id='app']/iframe"))); 未被更改,因此未在答案中列出。

简短回答:

我必须将Emulators.java添加到子模块的getter中,以便将所需的XML节点转换为所需的命名节点。另外,在主应用程序中,我必须遍历树并收集所有使用的类,以便可以将其传递给@XmlAnyElement(lax=true)

Application.java

JAXBContext

ModuleBase.java

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Application
{
    public static List<Class<?>> createClasses(ModuleBase oModule)
    {
        List<Class<?>> classes = new ArrayList<>();
        classes.add(oModule.getClass());

        for(ModuleBase module : oModule.getChildModules())
        {
            List<Class<?>> cls = createClasses(module);
            classes.addAll(cls);
        }

        return classes;
    }

    public static void main(String[] args)
    {
        try
        {
            Emulators emulators = new Emulators();
            emulators.addChild(new VICEModule("Id1", "VICE V1", "V1 Path", 6510));
            emulators.addChild(new VICEModule("Id2", "VICE V2", "V2 Path", 6511));

            StringWriter sw = new StringWriter();
            List<Class<?>> classes = createClasses(emulators);
            Class<?>[] cls = new Class<?>[classes.size()];
            for(int i = 0; i < classes.size(); i++)
                cls[i] = classes.get(i);

            JAXBContext jaxbContext = JAXBContext.newInstance(cls);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
            jaxbMarshaller.marshal(emulators, sw);
            String s = sw.toString();
            System.out.println(s);
        }
        catch(Throwable e)
        {
            System.err.println("Exception:"+e);
        }

        return;
    }
}

VICEModule.java

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;

public class ModuleBase
{
    private List<ModuleBase> mChilds = new ArrayList<>();

    private String mModuleId;
    private String mModuleName;

    public ModuleBase(String oModuleId, String oModuleName)
    {
        setModuleId(oModuleId);
        setModuleName(oModuleName);
    }

    public void addChild(ModuleBase oModuleNode)
    {
        mChilds.add(oModuleNode);
    }

    @XmlAnyElement(lax=true)
    public List<ModuleBase> getChildModules()
    {
        return mChilds;
    }

    @XmlAttribute
    public String getModuleId()
    {
        return mModuleId;
    }

    public void setModuleId(String oModuleId)
    {
        mModuleId = oModuleId;
    }

    @XmlAttribute
    public String getModuleName()
    {
        return mModuleName;
    }

    public void setModuleName(String oModuleName)
    {
        mModuleName = oModuleName;
    }
}

现在,XML可以完全按照预期的方式进行渲染,并存储派生类中的所有对象。

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "VICEModule")
public class VICEModule
    extends ModuleBase
{
    private String mInstallationPath;
    private int mPort;

    public VICEModule()
    {
        super("Id123456", "Vice");

        mPort = 6510;
    }

    public VICEModule(String oId, String oName, String oPath, int nPort)
    {
        super(oId, oName);

        setInstallationPath(oPath);
        setPort(nPort);
    }

    @XmlElement(name="InstallationPath")
    public String getInstallationPath()
    {
        return mInstallationPath;
    }

    public void setInstallationPath(String oPath)
    {
        mInstallationPath = oPath;
    }

    @XmlElement(name="Port")
    public int getPort()
    {
        return mPort;
    }

    public void setPort(int nPort)
    {
        mPort = nPort;
    }
}