I'm trying to use JAXB in my project and I found an issue that with Jibx was easy to handle (we stopped using it with jboss 7) but with JAXB we are having difficulties to solve.
So I have a class A as follows:
public class A
{
#XmlElement(name ="xpto")
public B fieldXPTO;
#XmlElement(name ="foo")
public B fieldFOO;
}
And my class B :
public class B
{
public String name;
public String address;
}
Now building our schema for class A we wanted to specify different names for the class B fields for each class A field. Something that when marshalling an A object would turn out like
<A>
<xpto>
<name_xpto>Peter</name_xpto>
<address_xpto>Moon Street</address_xpto>
</xpto>
<foo>
<name_foo>Ryan</name_foo>
<address_foo>Mars Street</address_foo>
</foo>
</A>
Is it possible to do this just by working with the schema ?
Thank you all,
Pedro
Related
I have a given XML file that I want to parse on an Android device. Since an XmlPullParser has proven too cumbersome to maintain, I would prefer to use Jackson for easier deserialization. JAXB is not supported on Android devices as internal sun.* classes are missing, so it has to be Jackson, as far as I understand.
The main object (of type A) is a container for an arbitrary number of elements, each of which is an instance of a subclass of the abstract class B. All child elements of A are to be deserialized into a list of B objects.
This is the XML structure:
<A>
<B1/>
<B2/>
</A>
And my POJO class structure looks like this:
public class A { public List<B> list; }
public abstract class B {}
public class B1 extends B {}
public class B2 extends B {}
And this is my Parser code:
InputStream in = context.getResources().openRawResource(R.raw.my_xml_file);
A a = new XmlMapper().readValue(in, A.class));
Pretty straightforward. But how do I have to annotate these classes with Jackson?
There are a lot of examples out there how to do it in JSON, and I tried to use #JsonTypeInfo and #JsonSubTypes to give the name of the subclasses.
public class A {
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({
#JsonSubTypes.Type(name="B1", value=B1.class),
#JsonSubTypes.Type(name="B2", value=B2.class)
})
public List<B> list;
}
public abstract class B {}
public class B1 extends B { #XmlTransient public final String type="B1"; }
public class B2 extends B { #XmlTransient public final String type="B2"; }
But this uses a type property, which I do not have and do not want. I want the element name to be used as type information. If I use JsonTypeInfo.As.WRAPPER_OBJECT, it expects an additional XML element, so my XML would have to look like this:
<A>
<B><B1/></B>
<B><B2/></B>
</A>
What am I missing?
I'm attempting to deserialize an XML payload (body of a SOAP message, but nothing else), with a specific hierarchy of tags / objects. When attempting to aggregate unwrapped objects into a List, a MismatchedInputException is thrown.
Example Payload
<libraryRequest>
<libraryProfile>
<libraryId>
<libraryName>
<newBookInfo>
<bookId>...</bookId>
<bookTitle>...</bookTitle>
<datePublished>...</datePublished>
</newBookInfo>
<currentBooks>
<bookId>...</bookId>
<bookTitle>...<bookTitle>
<datePublished>...</datePublished>
</currentBooks>
<currentBooks>
<bookId>...</bookId>
<bookTitle>...<bookTitle>
<datePublished>...</datePublished>
</currentBooks>
<currentBooks>...</currentBooks>
</libraryProfile>
</libraryRequest>
Java objects are
public class LibraryRequest {
private LibraryProfile libraryProfile;
#XmlElement(name = "libraryProfile")
public LibraryProfile getLibraryProfile(){
...
}
// setters
public class LibraryProfile {
// constructors, getters & setters for primitive types
private List<BookInfo> bookInfos;
public List<BookInfo> getBookInfo(){
return this.BookInfos;
}
// rest of the functions
My issue is that I don't know how many currentBooks tags will come in the XML payload, and they don't come in a wrapper element. I need to keep track of each currentBook element, which is why I was using a Collection, but I am not able to properly fill the collection with the information contained within the currentBooks tags.
Would I be able to use JAXB to group the XML sequence into a Java Collection/List, and if not would I be able to use Jackson's XML functionality to group the unwrapped XML tags into a Java Collection?
The main goal is to use have an XML request come into a Spring Controller and have the XML sequence properly deserialized into a Java List / Collection. Any advice would help.
I'm using Spring Boot 1.5.8 (later version was giving me trouble in a different way), and Jackson version 2.9.5
This is based on the XmlElement explanation from actimem.com.
The mechanics explained:
- #XmlElement is only needed if the field name is not equal to the xml tag name.
- If you would like to rename your field newBookInfo to newestBook but without changing the xml you'd simply rename your field and annotate it with #XmlElement(name="newBookInfo")
- #XmlElementWrapper is explicitly not used to advice JAXB it should search for the list tags directly in the parent node
The XML represenation classes Book
#XmlAccessorType(XmlAccessType.FIELD)
public static class Book {
private String bookId;
private String bookTitle;
// ... accessors and toString
}
and LibraryProfile
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public static class LibraryProfile {
private String libraryId;
private String libraryName;
private Book newBookInfo;
// the trick is NOT to use #XmlElementWrapper here
private List<Book> currentBooks;
private String foobar; // just to show a tag after the list
// ... accessors
}
The input based on your question (I skipped the <libraryRequest> to keep the example short)
<libraryProfile>
<libraryId>1</libraryId>
<libraryName>library of alexandria</libraryName>
<newBookInfo>
<bookId>42</bookId>
<bookTitle>the answer</bookTitle>
</newBookInfo>
<currentBooks>
<bookId>2</bookId>
<bookTitle>the second</bookTitle>
</currentBooks>
<currentBooks>
<bookId>1</bookId>
<bookTitle>the first</bookTitle>
</currentBooks>
<foobar>test-foo</foobar>
</libraryProfile>
And here the testing class:
package com.stackoverflow.answer;
import javax.xml.bind.JAXB;
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 java.io.StringReader;
public class Tester {
public static final String INPUT = "..."; // here goes your xml
public static void main(String[] args) throws Exception {
LibraryProfile lib = JAXB.unmarshal(new StringReader(INPUT), LibraryProfile.class);
System.out.println(lib.getLibraryName() + " currently contains");
System.out.println(lib.getCurrentBooks());
System.out.println("the newest book is: " + lib.getNewBookInfo());
}
}
The output is now
library of alexandria currently contains
[Book{bookId='2', bookTitle='the second'}, Book{bookId='1', bookTitle='the first'}]
the newest book is: Book{bookId='42', bookTitle='the answer'}
I need to map some legacy XML I can't change. There are several elements that have hundreds attributes exactly the same as some other elements. The attributes all have the same name postfixed with a number. So XML might look like this:
<someElement custom1="..." custom2="..." custom78=".."/>
<anotherElmenent custom1="..." custom45="..."/>
A solution that "works" is to create a base class like so:
#XmlAccessorType(FIELD)
public class LotsaCustomIds
{
#XmlAttribute
private String custom1;
#XmlAttribute
private String custom2;
...
}
#XmlType
public class SomeElement extends LotsaCustomIds
{
....
}
But it's a shame to use inheritence here, especially since Java only has single inheritence. What I'd like to do is something like the way JPA/Hibernate do embedded objects, like:
#XmlType
public class SomeElement
{
#EmbeddedAttributes
private LotsaCustomIds customIds;
....
}
Anyway to do this?
Note: I'm the EclipseLink JAXB (MOXy) lead.
You could use MOXy's #XmlPath extension to map this use case. When you use it as #XmlPath(".") then it will pull the contents of the child object (LotsaCustomIds) into the parent object (SomeElement).
#XmlType
public class SomeElement
{
#XmlPath(".")
private LotsaCustomIds customIds;
....
}
Related Information from my Blog
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
I'm using MOXy 2.5.1 along with Jersey 2.4 and Hibernate. I'm seeing a strange marshalling behavior with MOXy with a specific set of classes.
I have classes that look similar to this (with additional stuff removed for brevity):
public abstract class ListWrapper<M> {
protected List<M> records = new ArrayList<M>();
public abstract List<M> getRecords();
// plus functions to make it look like a List / Iterator
}
public abstract class RecordList<T extends Record> extends ListWrapper<T> {
protected Person person;
#XmlElement
public Person getPerson() { return person; }
}
public class FooList extends RecordList<Foo> {
#Override
#XmlElement(type=Foo.class)
public List<Foo> getRecords() { return super.records; }
}
public class Holder {
#XmlElement
public FooList getFooList() { /* ... */ }
}
When Holder gets marshalled to XML using MOXy I get the following:
<holder>
<fooList>
<records>...</records>
<records>...</records>
<person>...</person>
</fooList>
</holder>
I was expecting those <records> elements to be named <foos> instead.
If I put a name="foos" entry on the #XmlElement declaration in FooList I will get my expected name, but then MOXy does something else that's a bit strange. It will still output elements named <records> within the <fooList> element, but these <records> will only contain the <id> values for the (Hibernate) database entries that these correspond to. And so it looks like this:
<holder>
<fooList>
<records><id>5</id></records>
<records><id>10</id></records>
<person>...</person>
<foos>...</foos>
<foos>...</foos>
</fooList>
</holder>
It doesn't matter if I use an #XmlElementWrapper, a #XmlJavaTypeAdapter, or any other thing I can think of. I will either get the wrong names, or a second set of elements that contain a limited set of data. It's almost as though MOXy is not understanding the inheritance structure here or something.
I'll play around with a few other things in the meantime (e.g. ListWrapper API; it's not my design), but to me this looks like a bug in MOXy. Note that the same behaviors happen with JSON, so this isn't limited to XML.
I found a solution. I simply put #XmlAccessorType(XmlAccessType.NONE) on the ListWrapper and RecordList classes (it was already on the FooList class in my code). I think MOXy is (incorrectly?) looking up the tree to ListWrapper and creating an output entry based on that.
I have 2 classes:
#XmlRootElement
public class A {
private Long id;
private B b;
// setters and getters
}
and
#XmlRootElement
public class B {
private Long id;
private String field1;
private String field2;
// setters and getters
}
By default, if I transform an instance of class A to the XML, I will have all its fields (id) and the referenced B class fields (id, field1, field2) like this:
<a>
<id>2</id>
<b>
<id>5</id>
<field1>test1</field1>
<field2>test3</field2>
</b>
</a>
Is is possible to modify what fields from referenced class B are included in the XML of the A class? E.g. I want to say that when I transform an instance of A class, I just want to get id from the B class (no field1 and field2 fields), so I want to get:
<a>
<id>2</id>
<b>
<id>5</id>
</b>
</a>
I don't want to permanently annotate the B class (using #XMLTransient or #XMLElement) to achieve it, as there are cases in which I want to export whole B class as is (with id, field1 and field2.)
I just don't want to export all these fields when the B class is referenced from A.
Is this even possible with JAX-B?
You can use annotation #XmlTransient to ignore fields. Put this annotation on field itself or its getter.
You can use #XmlTransient on the field. Also the default JAXB bindings can be overridden at a global scope or on a case-by-case basis as needed by using custom binding declarations.
Check out the Guide to JAXB from Baeldung website for more examples.
Ok, I've come up with some hacky solution:
#XmlRootElement
public class A {
private Long id;
private B b;
// setters and getters
}
I've provided an additional getter just for the REST use case, so it's like:
#XMLTransient
public B getB() {
return b;
}
#XMLElement(name="b")
public Long getBForREST() {
return b.getId();
}
It results in the following structure:
<a>
<id>2</id>
<b>5</b>
</a>
It's not exactly same structure I aimed for it works for me.
I don't like this solution - an #XMLExclude({"field1", "field2"}) or something like that would be much cleaner in my opinion.
Nevertheless - for now on, it works; it's ugly, but it works.