JAXB использует @XmlJavaTypeAdapter для маршалирования подмножества коллекции
У меня есть объект, который содержит коллекцию объектов другого типа. То, что я хочу сделать, это дать JAXB маршалу только выборочное подмножество коллекции, основываясь на некоторых критериях.
@XmlRootElement
@Entity
public class A{
// other fields
@OneToMany(mappedBy = "x", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<B> bees;
@XmlJavaTypeAdapter(BFormatter.class)
public Collection<B> getBees() {
return bees;
}
public void setBees(Collection<B> bees) {
this.bees= bees;
}
}
@XmlRootElement
@Entity
public class B{
// fields
}
public class BFormatter extends XmlAdapter<Collection<B>, Collection<B>>{
@Override
public Collection<B> unmarshal(Collection<B> v) throws Exception {
return v;
}
@Override
public Collection<B> marshal(Collection<B> v) throws Exception {
Collection<B> subset;
// making subset
return subset;
}
}
Это приводит к ошибкам, говорящим "java.util.Collection является интерфейсом, а JAXB не может обрабатывать интерфейсы" и что "java.util.Collection не имеет конструктора по умолчанию без аргументов".
Что я делаю не так, и это даже правильный путь?
1 ответ
Важно то, что вы не можете адаптировать Collection (интерфейс) к тому, что может обрабатывать JAXB, так как он не выполняет маршализацию ArrayList или какого-либо другого класса коллекции. Он предназначен для маршалирования (bean) классов, содержащих поля, которые являются списками или подобными, что означает "исчезать", оставаясь простым повторением его элементов. Другими словами, отсутствует элемент XML, представляющий сам ArrayList (или любой другой).
Следовательно, адаптер должен модифицировать содержащий элемент. (См. Ниже альтернативы.) Следующие классы работают; Просто соберите элемент Root и измените AFormatter в соответствии с вашим дизайном. (Комментарии относятся к примеру по адресу https://jaxb.java.net/tutorial/section_6_2_9-Type-Adapters-XmlJavaTypeAdapter.html.)
(Большинство классов должны быть изменены, чтобы не делать поля общедоступными, но как бы то ни было, они кратки и работают.)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root{ // Training
@XmlElement
private A a; // Brochure
public Root(){}
public A getA(){ return a; }
public void setA( A value ){ a = value; }
}
@XmlJavaTypeAdapter(AFormatter.class)
public class A{ // Brochure
private Collection<B> bees;
public A(){
bees = new ArrayList<>();
}
public Collection<B> getBees() {
if( bees == null ) bees = new ArrayList<>();
return bees;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
public class B{ // Course
@XmlElement
private String id;
public B(){}
public String getId(){ return id; }
public void setId( String value ){ id = value; }
}
public class AFormatter extends XmlAdapter<BeeHive, A>{
@Override
public A unmarshal(BeeHive v) throws Exception {
A a = new A();
for( B b: v.beeList ){
a.getBees().add( b );
}
return a;
}
@Override
public BeeHive marshal(A v) throws Exception {
BeeHive beeHive = new BeeHive();
for( B b: v.getBees() ){
if( b.getId().startsWith("a") ) beeHive.beeList.add( b );
}
return beeHive;
}
}
public class BeeHive { // Courses
@XmlElement(name="b")
public List<B> beeList = new ArrayList<B>();
}
Альтернативы: Было бы довольно просто, если бы обычный получатель B-списка возвращал те, которые должны быть упорядочены. Если приложение должно видеть все, можно добавить альтернативный метод получения. Или у класса может быть статический флаг, который указывает получателю возвращать список, который будет использоваться для сортировки, или обычный список в другое время.