Сбой сериализации JAXB с общим классом - PullRequest
0 голосов
/ 11 сентября 2018

Я пытаюсь написать класс, который может сортировать и десерилизовать настройки в XML с использованием Java.У меня этот код успешно написан на C #, и он очень полезен, поэтому я хотел бы что-то подобное в моем Java-приложении.

У меня есть следующий базовый класс, который должен реализовать каждый класс, который я хочу сериализовать в XML.

package serializers;

import java.lang.reflect.ParameterizedType;

abstract class XmlSerializableObject<T> {

    abstract T getDefault();

    abstract String getSerializedFilePath();

    String getGenericName() {
        return ((Class<T>) ((ParameterizedType) getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
    }

    ClassLoader getClassLoader() {
        return ((Class<T>) ((ParameterizedType) getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]).getClassLoader();
    }
}

, где getGenericName и getClassLoader предназначены для использования с экземпляром JAXBContext.Затем у меня есть базовая реализация этого в качестве поставщика настроек

public class SettingsProvider extends XmlSerializableObject<SettingsProvider> {

    private Settings settings;

    @Override
    public SettingsProvider getDefault() {
        return null;
    }

    @Override
    public String getSerializedFilePath() {
        return "C:\\Data\\__tmp.settings";
    }

    public Settings getSettings() {
        return settings;
    };

    public void setSettings(Settings settings) {
        this.settings = settings;
    }
}

class Settings {

    private String tmp;

    public String getTmp() {
        return tmp;
    }

    public void setTmp(String tmp) {
        this.tmp = tmp;
    }
}

Теперь у меня есть следующий класс сериализатора

package serializers;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;

public class XmlSerializer {

    private static final Logger logger = LoggerFactory.getLogger(XmlSerializer.class);

    public static <T extends XmlSerializableObject> void Serialize(T o) {

        String filePath = o.getSerializedFilePath();
        File file = new File(filePath);

        try {
            String name = o.getGenericName();
            ClassLoader classLoader = o.getClassLoader();

            // THE FOLLOWING LINE throws.
            JAXBContext jaxbContext = JAXBContext.newInstance(name, classLoader); // also tried JAXBContext.newInstance(name);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbMarshaller.marshal(o, file);
        } catch (JAXBException e) {
            logger.error("Serialization failed", e);
        }
    }

    // Deserialize below.
}

У меня есть следующий тест для проверки результатов сериализации

package serializers;

import org.junit.Before;
import org.junit.Test;

public class XmlSerializerTest {

    private Settings settings = new Settings();
    private SettingsProvider provider;

    @Before
    public void setUp() throws Exception {
        settings.setTmp("testing");
        provider = new SettingsProvider();
        provider.setSettings(settings);
    }

    @Test
    public void serialize() throws Exception {
        XmlSerializer.Serialize(provider);
    }
}

Проблема заключается в вызове JAXBContext jaxbContext = JAXBContext.newInstance(name, classLoader);, который выдает

javax.xml.bind.JAXBException: поставщик com.sun.xml.internal.bind.v2.ContextFactoryне может быть создан: javax.xml.bind.JAXBException: «serializers.SettingsProvider» не содержит ObjectFactory.class или jaxb.index - со связанным исключением: [javax.xml.bind.JAXBException: «serializers.SettingsProvider» не содержит ObjectFactory.class или jaxb.index]

Я пробовал с и без объекта ClassLoader безрезультатно.Как я могу сериализовать универсальный тип таким способом?

Спасибо за ваше время.

Ответы [ 4 ]

0 голосов
/ 11 октября 2018

Это было сделано с помощью следующих интерфейсов

public interface IXmlSerializableObject {
    String getSerializedFilePath();
}

Важнейшим из них является

public interface IPersistanceProvider<T> extends IXmlSerializableObject {
    void save();
    void restoreDefaults();
    Class<T> getTypeParameterClass();
}

Важнейшим свойством является Class<T> getTypeParameterClass().Затем это используется в

public static <T extends PersistanceProviderBase> void Serialize(T o) {
    String filePath = o.getSerializedFilePath();
    File file = new File(filePath);
    try {
        JAXBContext jaxbContext = JAXBContext.newInstance(o.getTypeParameterClass());
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.marshal(o, file);
    } catch (JAXBException e) {
        logger.error("Serialization failed", e);
    }
}

, где PersistanceProviderBase реализует интерфейс IPersistanceProvider.

0 голосов
/ 08 октября 2018

Вы используете этот newInstance метод :

Параметры:

  • contextPath - список имен пакетов java , которые содержат производный класс схемы и / или сопоставленные классы java-схемы (аннотированные JAXB) * ​​1013 *

  • classLoader - этот загрузчик классов будет использоваться для поиска классов реализации.

Так что df778899 прав, вы не должны использовать эту подпись, так как getGenericName возвращает полное имя класса, а не пакет. И даже если это был пакет, вы все равно пропустите ObjectFactory.class или jaxb.index

Но JAXBContext.newInstance(SettingsProvider.class) тоже не сработает. Вы получите MarshalException, указывающее, что @XmlRootElement отсутствует

Аннотировать SettingsProvider как это:

@XmlRootElement(name = "root")
static class SettingsProvider extends XmlSerializableObject<SettingsProvider>
{

    private Settings settings;

    // [...]

И, наконец, вы получите:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <settings>
        <tmp>testing</tmp>
    </settings>
</root>
0 голосов
/ 10 октября 2018

Давайте посмотрим на строку кода, которая выдает исключение:

JAXBContext jaxbContext = JAXBContext.newInstance(name);

В приведенной выше строке кода аргумент name, который вы передаете, является именем класса, которыйбыть десериализованным и определенным во время выполнения (а именно, serializers.SettingsProvider в данном образце).Этого может быть недостаточно для JAXB, чтобы определить все классы, составляющие контекст JAXB.Поэтому вместо этого попробуйте передать имя пакета (ов), которые содержат все классы, которые этот дескриптор JAXBContext должен десериализовать - все классы в этом пакете (ах) - это ваш JAXB-контекст .Это то, что будет известно во время компиляции.Итак, вместо этого попробуйте следующую строку кода:

JAXBContext jaxbContext = JAXBContext.newInstance("serializers");

Здесь «сериализаторы» - это имя пакета, содержащего все классы, которые вы хотите десериализовать, т. Е. контекст JAXBдля данного образца .

Вы можете обратиться к учебнику Oracle JAXB и отметить следующие строки кода:

import primer.po.*;

...

JAXBContext jc = JAXBContext.newInstance( "primer.po" );


Пожалуйста, обратитесь к этому Javadoc и обратите внимание, что в случае десериализации классов, распределенных по нескольким пакетам, тогда список пакетов, разделенных двоеточиямиимена должны быть переданы, например, -

JAXBContext.newInstance( "com.acme.foo:com.acme.bar" ) 


Если вы должны передать имена классов вместо имен пакетов, то сначала внимательно прочитайте этот Javadoc .Обратите внимание, что экземпляр JAXBContext будет инициализирован только с классами, переданными в качестве параметра, и классами, которые статически доступны из этих классов.Предпочитайте писать свою программу таким образом, чтобы передаваемые имена классов были известны во время компиляции.

Кроме того, вам может быть полезно заметить, что универсальные в Java разные (особенно в отношении типа стирание)), чем в C # - см. Какова концепция стирания в дженериках в Java? .

Кроме того, учитывая объявление класса:

class XmlSerializableObject<T> {
}

, в котором говорится, что класс XmlSerializableObject имеет дело с типом T, следующее объявление класса:

class SettingsProvider extends XmlSerializableObject<SettingsProvider> {
}

в котором говорится, что класс SettingsProvider имеет дело со своими собственными типами звуков, запутанными.

Или вы вместо этого подразумеваете, что он объявляется следующим образом:

class SettingsProvider extends XmlSerializableObject<Settings> {
}

, в котором говорится, что класс SettingsProvider сделок с типом Settings?

0 голосов
/ 07 октября 2018

Похоже, что это должно быть JAXBContext.newInstance(SettingsProvider.class).

. * * * * * * В версиях метода JAXBContext.newInstance(String ...) ожидается имя пакета, которое, как говорится в сообщении об ошибке, должно содержать класс ObjectFactory, или jaxb.index список, чтобы вести его к классам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...