Jaxb с базовым классом не сериализует производные элементы - PullRequest
0 голосов
/ 09 октября 2018

У меня есть список модулей в древовидной структуре.Базовый класс содержит дочерние и некоторые общие свойства, в то время как производный класс должен предоставлять свои отдельные поля.Теперь, когда я пытаюсь сериализовать это с 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 голосов
/ 09 октября 2018

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

Краткий ответ:

Мне пришлось добавить @XmlAnyElement(lax=true) в мой геттер для дочерних модулей, чтобы преобразовать полученный результатXML-узел в именованный узел, как я хочу.Кроме того, в основном приложении мне пришлось пройти по дереву и собрать все используемые классы, чтобы я мог передать его в JAXBContext.

Application.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;
    }
}

ModuleBase.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;
    }
}

VICEModule.java

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;
    }
}

Теперь XML отображается точно так, как задумано, с сохранением всех объектов из производных классов.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators moduleId="IdEmu129872q53" moduleName="Emulators">
    <VICEModule moduleId="Id1" moduleName="VICE V1">
        <InstallationPath>V1 Path</InstallationPath>
        <Port>6510</Port>
    </VICEModule>
    <VICEModule moduleId="Id2" moduleName="VICE V2">
        <InstallationPath>V2 Path</InstallationPath>
        <Port>6511</Port>
    </VICEModule>
</Emulators>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...