JAXB универсальный @XmlValue - PullRequest
8 голосов
/ 10 января 2012

Цель состоит в том, чтобы создать следующий XML с JAXB

<foo>
   <bar>string data</bar>
   <bar>binary data</bar>
</foo>

Есть ли обходной путь для разрешения общих @XmlValue полей (мне нужно хранить данные byte[] и String)? Ниже то, что я желаю:

@XmlRootElement
public class Foo {
    private @XmlElement List<Bar> bars;
}

@XmlRootElement
public class Bar<T> {
    private @XmlValue T value;  // (*)
}

Но я получаю это исключение

(*) IllegalAnnotationException:
@ XmlAttribute / @ XmlValue должен ссылаться на тип Java, который отображается на текст в XML.

Ответы [ 5 ]

8 голосов
/ 18 января 2012

Вы можете использовать XmlAdapter для этого варианта использования вместо @XmlValue:

BarAdapter

package forum8807296;

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

public class BarAdapter extends XmlAdapter<Object, Bar<?>> {

    @Override
    public Bar<?> unmarshal(Object v) throws Exception {
        if(null == v) {
            return null;
        }
        Bar<Object> bar = new Bar<Object>();
        bar.setValue(v);
        return bar;
    }

    @Override
    public Object marshal(Bar<?> v) throws Exception {
        if(null == v) {
            return null;
        }
        return v.getValue();
    }

}

Foo

XmlAdapter связан со свойством bars, используя аннотацию @XmlJavaTypeAdapter:

package forum8807296;

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Foo {
    private List<Bar> bars;

    @XmlElement(name="bar")
    @XmlJavaTypeAdapter(BarAdapter.class)
    public List<Bar> getBars() {
        return bars;
    }

    public void setBars(List<Bar> bars) {
        this.bars = bars;
    }

}

Бар

package forum8807296;

public class Bar<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

Демо

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

package forum8807296;

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

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

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Foo.class);

        Foo foo = new Foo();
        List<Bar> bars = new ArrayList<Bar>();
        foo.setBars(bars);

        Bar<String> stringBar = new Bar<String>();
        stringBar.setValue("string data");
        bars.add(stringBar);

        Bar<byte[]> binaryBar = new Bar<byte[]>();
        binaryBar.setValue("binary data".getBytes());
        bars.add(binaryBar);

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

}

выход

Обратите внимание, что выходные данные включают атрибуты xsi:type для сохранения типа значения. Вы можете устранить атрибут xsi:type, указав XmlAdapter return String вместо Object. Если вы сделаете это, вам потребуется самостоятельно выполнить преобразование из String в соответствующий тип для операции unmarshal: 1042 *

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo>
    <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">string data</bars>
    <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:base64Binary">YmluYXJ5IGRhdGE=</bars>
</foo>
4 голосов
/ 17 января 2012

Я не мог заставить @XmlValue работать, так как всегда получал NullPointerException по пути - не знаю почему. Вместо этого я придумал что-то вроде следующего.

Отбросьте свой класс Bar полностью, потому что, если вы хотите, чтобы он мог содержать что-либо , вы можете просто представить его с помощью Object.

@XmlRootElement(name = "foo", namespace = "http://test.com")
@XmlType(name = "Foo", namespace = "http://test.com")
public class Foo {

  @XmlElement(name = "bar")
  public List<Object> bars = new ArrayList<>();

  public Foo() {}
}

Не сообщая JAXB, в каких пространствах имен ваши типы используют каждый элемент bar внутри foo, он будет содержать отдельные объявления и прочее пространства имен - package-info.java и все вещи пространства имен служат только для фантазии .

@XmlSchema(attributeFormDefault = XmlNsForm.QUALIFIED,
           elementFormDefault = XmlNsForm.QUALIFIED,
           namespace = "http://test.com",
           xmlns = {
               @XmlNs(namespaceURI = "http://test.com", prefix = ""),
               @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
               @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema", prefix = "xs")})
package test;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

Выполнение этого простого теста вылилось бы в нечто похожее на ваш фрагмент XML.

public static void main(String[] args) throws JAXBException {
  JAXBContext context = JAXBContext.newInstance(Foo.class);

  Foo foo = new Foo();
  foo.bars.add("a");
  foo.bars.add("b".getBytes());

  Marshaller marshaller = context.createMarshaller();
  marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
  marshaller.marshal(foo, System.out);
}

Выход:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <bar xsi:type="xs:string">a</bar>
    <bar xsi:type="xs:base64Binary">Yg==</bar>
</foo>
0 голосов
/ 09 апреля 2015

https://stackoverflow.com/a/3074027/1851289 может быть дубликатом, однако у ответа есть еще 2 варианта.

0 голосов
/ 18 января 2012

Уловка, которую я обычно использую, состоит в том, чтобы создать схему с типами, которые вы хотите, а затем использовать xjc для генерации классов Java и посмотреть, как используются аннотации. :) Я верю в правильное отображение типа схемы XML для byte [] - base64Binary, поэтому создаем схему следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified">
    <element name="aTest" type="base64Binary"></element>
</schema>

и, запустив xjc, мы получим следующий код:

@XmlElementDecl(namespace = "http://www.example.org/NewXMLSchema", name = "aTest")
public JAXBElement<byte[]> createATest(byte[] value) {
    return new JAXBElement<byte[]>(_ATest_QNAME, byte[].class, null, ((byte[]) value));
}
0 голосов
/ 16 января 2012

Есть ли причина, по которой вы не просто строите строку со своим байтом []? Вам действительно нужен дженерик?

...