Поведение добавления / переопределения в классах, сгенерированных Jaxb, путем их расширения - PullRequest
1 голос
/ 26 сентября 2011

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

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement(name = "image")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        // some complex computation
        return uri;
    }
}

Когда я пытаюсь разархивировать ответ от сервера в этот объект, так как нет никакого установщика для absoluteUri, у меня нет imageUri в классе. Поэтому я расширяю это так:

public class FEImage extends Image{
private String imageUri;
    public String getAbsoluteUri() {
        return imageUri;
    }
    public void setAbsoluteUri(String imageUri) {
        this.imageUri = imageUri;
    }   
}

Мой ObjectFactory

@XmlRegistry
public class ObjectFactory {
    public Image createImage(){
        return new FEImage();
    }
}

Мой код для демаршал здесь:

JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());         
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();

Однако, setAbsoluteUri, кажется, не вызывается в FEImage во время демаршаллинга. Когда я добавляю манекен setAbsoluteUri в Image.java, все работает как положено.

Может кто-нибудь сказать мне, как я могу чисто вытянуть из Image.java?

1 Ответ

4 голосов
/ 26 сентября 2011

Примечание: Я EclipseLink JAXB (MOXy) и являюсь членом JAXB 2 (JSR-222) экспертная группа.

Реализация JAXB не требуется использовать класс ObjectFactory при создании экземпляра объекта.Вы можете настроить создание экземпляра через фабричный класс, используя аннотацию @XmlType:

@XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        // some complex computation
        return uri;
    }
}

Если вы выполните вышеизложенное, то вашРеализация JAXB по-прежнему будет использовать класс Image для получения метаданных, поэтому она не решит вашу проблему.Альтернативный подход заключается в использовании XmlAdapter для этого варианта использования:

Еще лучше, если свойство объекта вашего домена не имеетустановщик, вы можете указать вашей реализации JAXB ( EclipseLink MOXy , Metro, Apache JaxMe и т. д.) использовать вместо доступа к полю (переменной экземпляра) @XmlAccessorType(XmlAccessType.FIELD):

@XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}

ОБНОВЛЕНИЕ # 1

Если вы не можете изменять доменные объекты, вас могут заинтересовать внешние метаданные MOXy.Это расширение предоставляет средства через XML для предоставления метаданных JAXB для классов, в которых нельзя изменить источник.

Для получения дополнительной информации

ОБНОВЛЕНИЕ № 2 - По результатам чата

Изображение

Ниже приведена реализация Image класс, который я буду использовать для этого примера.Для сложных вычислений getAbsoluteUri() я просто добавляю префикс "CDN" к имени файла:

package forum7552310;

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

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement(name = "image")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        return "CDN" + filename;
    }

}

binding.xml

Ниже приведен связывающий документ MOXy, который я собрал.В этом файле я делаю несколько вещей:

  • Установите XmlAccessorType на FIELD
  • Отметьте свойство absoluteURI равным XmlTransient, так как мы будем отображать filenameВместо этого.
  • Укажите, что XmlAdapter будет использоваться с полем filename.Это делается для применения логики, которая выполняется в методе getAbsoluteUri().

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum7552310">
    <java-types>
        <java-type name="Image" xml-accessor-type="FIELD">
            <java-attributes>
                <xml-element java-attribute="filename" name="imageUri">
                    <xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
                </xml-element>
                <xml-transient java-attribute="absoluteUri"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

FileNameAdapter

Ниже приведена реализация XmlAdapter, которая применяет тот же алгоритм имен, что и метод getAbsoluteUri():

package forum7552310;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class FileNameAdapter extends XmlAdapter<String, String> {

    @Override
    public String marshal(String string) throws Exception {
        return "CDN" + string;
    }

    @Override
    public String unmarshal(String adaptedString) throws Exception {
        return adaptedString.substring(3);
    }

}

Демо

Ниже приведен демонстрационный код, демонстрирующий, как применить файл связывания при создании JAXBContext:

package forum7552310;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

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

import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum7552310/binding.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);

        File xml = new File("src/forum7552310/input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Image image = (Image) unmarshaller.unmarshal(xml);

        System.out.println(image.getAbsoluteUri());

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(image, System.out);
    }
}

jaxb.properties

Вам необходимо включить файл с именем jaxb.properties со следующим содержимым в тот же пакет, что и ваш Image класс:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

input.xml

Вот XML-вход, который я использовал:

<?xml version="1.0" encoding="UTF-8"?>
<image>
    <imageUri>CDNURI</imageUri>
</image>

Выход

А вот результат запуска демокод:

CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
   <imageUri>CDNURI</imageUri>
</image>
...