Почему MOXy JAXB маршалирует пустую строку, а эталонная реализация JAXB — нет?

Показанный ниже тест JUnit не работает (в моей среде) с MOXy, но работает с эталонной реализацией JAXB.

Ожидаемый результирующий XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<itemManager>
    <items>
        <value>2</value>
    </items>
</itemManager>

но с включенным MOXy - jaxb.properties, содержащий:

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

результирующая строка XML пуста.

В чем может быть причина такого поведения и что нужно сделать, чтобы это исправить?

Я пробовал EclipseLink MOXy версий 2.5.1 и 2.4.2 — см. фрагмент pom.xml ниже. Зависимость является первой в pom.xml, за которой следуют многие другие (которые также могли испортить шоу).

<!-- Eclipselink moxy -->
<dependency>
  <groupId>org.eclipse.persistence</groupId>
  <artifactId>org.eclipse.persistence.moxy</artifactId>
  <version>2.4.2</version> 
  <!--  <version>2.5.1</version>   -->
  <scope>test</scope>
</dependency>

Java-код теста JUnit:

package com.bitplan.storage.moxy;

import static org.junit.Assert.*;

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

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.junit.Test;

/**
 * show problem with empty xml result for Jaxb Marshaller
 * 
 * @author wf
 * 
 */
public class TestEmptyXml {

    public interface Copyable<T> {
        /**
         * copy me from the copyable
         * 
         * @param othe
         *          - the source copyaqble to copy from
         */
        public void copyFrom(T other);
    }

    public interface BO<BO_T> extends Copyable<BO_T> {

    }

    public interface ItemInterface {

    }

    @XmlRootElement
    public static class Item implements ItemInterface {
        int value;

        /**
         * @return the value
         */
        public int getValue() {
            return value;
        }

        /**
         * @param value
         *          the value to set
         */
        public void setValue(int value) {
            this.value = value;
        }

        public Item() {

        }

        public Item(int pValue) {
            value = pValue;
        }

    }

    public interface ItemManagerInterface {
        public void setItems(List<Item> pItems);

        public List<Item> getItems();
    }

    public interface MoxyList<BO_T> extends Collection<BO_T> {

    }

    public interface MoxyManager<BO_T> extends MoxyList<BO_T> {
    }

    public static class MoxyManagerImpl<BO_T> implements MoxyManager<BO_T> {
        public transient List<BO_T> bolist = new ArrayList<BO_T>();

        @Override
        public boolean add(BO_T arg0) {
            boolean result = bolist.add(arg0);
            return result;
        }

        @Override
        public boolean addAll(Collection<? extends BO_T> arg0) {
            boolean result = bolist.addAll(arg0);
            return result;
        }

        @Override
        public void clear() {
            bolist.clear();
        }

        @Override
        public boolean contains(Object arg0) {
            boolean result = bolist.contains(arg0);
            return result;
        }

        @Override
        public boolean containsAll(Collection<?> arg0) {
            boolean result = bolist.containsAll(arg0);
            return result;
        }

        @Override
        public boolean isEmpty() {
            boolean result = bolist.isEmpty();
            return result;
        }

        @Override
        public Iterator<BO_T> iterator() {
            Iterator<BO_T> result = bolist.iterator();
            return result;
        }

        @Override
        public boolean remove(Object arg0) {
            boolean result = bolist.remove(arg0);
            return result;
        }

        @Override
        public boolean removeAll(Collection<?> arg0) {
            boolean result = bolist.removeAll(arg0);
            return result;
        }

        @Override
        public boolean retainAll(Collection<?> arg0) {
            boolean result = bolist.retainAll(arg0);
            return result;
        }

        @Override
        public int size() {
            int result = bolist.size();
            return result;
        }

        @Override
        public Object[] toArray() {
            Object[] result = bolist.toArray();
            return result;
        }

        @Override
        public <T> T[] toArray(T[] arg0) {
            T[] result = bolist.toArray(arg0);
            return result;
        }

    }

    @XmlAccessorType(XmlAccessType.NONE)
    @XmlTransient
    public static abstract class MyMoxyManager<BO_T> extends
            MoxyManagerImpl<BO_T> {
        public abstract Class<? extends BO<?>> getEntityType();

    }

    @XmlRootElement
    public static class ItemManager extends MyMoxyManager<Item> implements
            ItemManagerInterface {
        public ItemManager() {

        }

        private List<Item> items = new ArrayList<Item>();

        @Override
        @XmlElement
        public void setItems(List<Item> pItems) {
            items = pItems;
        }

        @Override
        public List<Item> getItems() {
            return items;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Class<? extends BO<?>> getEntityType() {
            return (Class<? extends BO<?>>) Item.class;
        }
    }

    /**
     * convert the target to XML Format
     * 
     * @param target
     * @return
     * @throws Exception
     */
    public String asXML(Object target) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(target.getClass());
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        // output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        StringWriter sw = new StringWriter();
        jaxbMarshaller.marshal(target, sw);
        String result = sw.toString();
        return result;
    }

    @Test
    public void testMarshall() throws Exception {
        ItemManager im = new ItemManager();
        im.items.add(new Item(2));
        String xml = asXML(im);
        System.out.println(xml);
        assertNotEquals("xml should not be empty", "", xml);
    }

}

person Wolfgang Fahl    schedule 13.10.2013    source источник


Ответы (1)


В вашем примере маршалируемый параметр target является экземпляром ItemManager. Из-за следующей организации классов MOXy в конечном итоге считает, что ItemManager является коллекцией, а не объектом домена, поэтому и происходят странные вещи.

  • ItemManager -расширяется-> MyMoxyManager
  • MyMoxyManager -расширяется-> MoxyManagerImpl
  • MoxyManagerImpl -орудия-> MoxyManager
  • MoxyManager -расширяется-> MoxyList
  • MoxyList -расширяется-> Collection

Мне все еще нужно исследовать, каким должно быть правильное поведение для этого варианта использования. Не могли бы вы пойти дальше и открыть ошибку для этой проблемы.

person bdoughan    schedule 15.10.2013
comment
Спасибо - отладив исходный код, я обнаружил такое же поведение. Пожалуйста, следите за моим отчетом об ошибке - это займет несколько дней. - person Wolfgang Fahl; 16.10.2013