Getting multiple XML elements with the same name JAXB - java

probably a stupid question, but I'm stuck.
I try do parse a huge xml document retrieved from a REST service.
What I'm interested in are both the abstract parts.
<article article-type="research-article">
<front>
<article-meta>
<abstract></abstract>
<abstract abstract-type="summary"></abstract>
</article-meta>
</front>
</article>
In my front class, I do the following:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Front {
#XmlElementWrapper(name = "article-meta")
#XmlElement(name="abstract")
private List<AuthorSummary> authorSummaries = new ArrayList<AuthorSummary>();
/** Getter and Setter **/
}
Sadly, I only the get the first abstract, but there the content as well. You can see my AuthorSummary Class below.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class AuthorSummary {
#XmlElement(name = "title")
private String title;
#XmlElement(name = "p")
private String content;
#XmlAttribute(name = "abstract-type")
private String abstractType;
/** Getter and Setter **/
}
So, I'm stuck and would be very glad for any hints.
Thank you very much

I have a solution, but it is not using jaxb or even data binding at all. So if you are stuck to data binding I will delete my answer. Otherwise, I like you point to data projection (Disclosure: I am affiliated with that project) instead of data binding:
public class ReadElementsWithSameName {
public interface Article {
#XBRead("./#article-type")
String getType();
#XBRead("./front/article-meta/abstract")
List<String> getAllAbstracts();
#XBRead("./front/article-meta/abstract[#abstract-type='summary']")
String getSummary();
}
// Configure the underlying document builder to not load the (nonexisting) DTD
private static final XMLFactoriesConfig nonValidatingConfig = new DefaultXMLFactoriesConfig() {
#Override
public DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory factory = super.createDocumentBuilderFactory();
try {
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
return factory;
}
};
public static void main(String... args) throws IOException {
List<Article> articles = new XBProjector(nonValidatingConfig).io().url("res://data.xml").evalXPath("/article").asListOf(Article.class);
for (Article article:articles) {
System.out.println(article.getType());
System.out.println(article.getSummary());
System.out.println(article.getAllAbstracts());
}
}
}
Instead of reflecting the XML structure with java classes, just define the Java API as a "projection interface" with the accessors (and setters) you like to have. Afterwards, let the projection framework take care of reading and writing your changes to the DOM.

Related

How to modify existing annotation through spoon in java

I am trying to modify my models and remove deprecated annotations. Explained below.
Current Class
Class User{
#Column(name="Name",percision = 3,constraint = "constraint")
private String name;
}
After modification
#Column(name="Name",percision = 3)
private String name;
}
As constraint field in deprecated in annotations I want to remove it keeping rest of the things same.
I am not able to figure out how to do that in spoon.
If any one could help me please share snippets or examples if you have.
Tried editing below code but was not able to retrieve existing annotation and edit.
public class AddNullable {
public static void main(String[] args) {
Launcher spoon = new Launcher();
spoon.addInputResource("field/HasField.java");
spoon.getEnvironment().setAutoImports(true);
spoon.setSourceOutputDirectory(new File("output"));
CtModel model = spoon.buildModel();
Factory factory = spoon.getFactory();
CtAnnotationType nullableAnnotation = (CtAnnotationType) factory.Type().get(Nullable.class);
CtType hasField = model.getRootPackage().getType("HasField");
List<CtField> fields = hasField.getFields();
System.out.println(fields.size());
for (CtField field : fields) {
if (!field.hasAnnotation(Nullable.class) && !field.getType().isPrimitive()) {
System.out.println(field.getSimpleName() + " does not has annotation.");
CtAnnotation an = factory.createAnnotation(nullableAnnotation.getReference());
field.addAnnotation(an);
}
}
spoon.prettyprint();
}
}

How to parse to different classes based on condition in XML using JAXB?

I have a Java application using JAXB for XML parsing. I need to parse one of the subtags to different JAXB classes based on the value of another tag (think a "version" tag, resulting in slightly different XML formats).
Here is code for Request JAXB class which I am parsing:
#XmlRootElement(name = "request")
#XmlAccessorType(XmlAccessType.FIELD)
public class Request {
#XmlElement(name = "fields1")
private List<Field1> fields1;
#XmlElement(name = "problemTag")
#XmlJavaTypeAdapter(value = JavaBeanAdapter.class)
private List<BaseClass> problemTag;
...
Here are some details of what I've got so far:
In my code I have an IXmlProcessor interface. Its implementation contains JAXB related logic (marshalling and unmarshalling).
I wrote an XmlAdapter for my problem tag. It adds extra complexity of using two additional IXmlProcessor instances with different JAXB contexts.
I declared this XmlAdapter as a Spring bean.
I inject this XmlAdapter bean inside my main IXmlProcessor implementation, when creating javax.xml.bind.Unmarshaller.
BaseClass is JAXB class for 'older' version of XML and ExtendingClass with additional fields for a newer version; ExtendingClass of course extends BaseClass
So, the flow in my test implementation is like this:
call IXmlProcessor#read(String xml, Class<Z> clazz)
inside of javax.xml.bind.Unmarshaller#unmarshal my custom XmlAdapter will be called.
In turn, XmlAdapter will call it's own instances of IXmlProcessor to parse String to BaseClass or ExtendingClass.
Here is code example:
public class JavaBeanAdapter extends XmlAdapter<Object, BaseClass> {
private IXmlProcessor xmlOld;
private IXmlProcessor xmlNew;
//IXmlProcessor, holding JAXBContext with BaseClass
public void setXmlOld(IXmlProcessor xml) {
this.xmlOld = xml;
}
//IXmlProcessor, holding JAXBContext with ExtendingClass
public void setXmlNew(IXmlProcessor xml) {
this.xmlNew = xml;
}
//First, I parse org.w3c.dom.Element back to string.
//Than I can inspect raw XML.
//This is just test solution, obviously too sloppy to be used in prod, just to get an insight to what i want to achieve
#Override
public BaseClass unmarshal(Object v) {
if (v == null) return null;
String rawXmlTag = xmlNew.asStringNonRoot(v, Object.class, ((Element) v).getTagName());
BaseClass req;
if (rawXmlTag.contains("someSPecificTagForNewVersionOfXml")) {
req = xmlNew.read(rawXmlTag, ExtendingClass.class);
} else {
req = xmlOld.read(rawXmlTag, BaseClass.class);
}
return req;
}
#Override
public Object marshal(BaseClass v) throws Exception {
if (v == null) return null;
else if (v instanceof ExtendingClass) return xmlNew.asStringNonRoot((ExtendingClass) v,
ExtendingClass.class, "BaseClass");
else return xmlOld.asStringNonRoot(v, BaseClass.class, "BaseClass");
}
}
Then I manually add this adapter to a unmarshaller inside my main IXmlProcessor implementation.
Of course, I am not satisfied by rawXmlTag.contains(...).
Part of the IXmlProcessor implementation code (for understanding of what going there):
public <Z> Z read(String xml, Class<Z> clazz) {
try {
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xml));
return genUnmarshaller(clazz).unmarshal(xmlStreamReader, clazz).getValue();
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
}
}
#Override
public <Z> String asStringNonRoot(Z xmlObj, Class<Z> clazz, String rootName) {
try {
StringWriter strWriter = new StringWriter();
Marshaller marshaller = genMarshaller(xmlObj);
QName qName = new QName(null, rootName);
JAXBElement<Z> root = new JAXBElement<Z>(qName, clazz, xmlObj);
marshaller.marshal(root, strWriter);
return strWriter.toString();
} catch (Throwable e) {
log.error("Error serializing object to XML string: {}", ExceptionUtils.getStackTrace(e));
throw new IllegalStateException("cannot serialize object to XML");
}
}
#Override
public <Z> Z read(org.w3c.dom.Document xml, Class<Z> clazz) {
try {
return genUnmarshaller(clazz).unmarshal(xml, clazz).getValue();
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
}
}
My questions are following:
Is there any alternative solution to address this issue without adding additional IXmlProcessor instances with different JAXB contexts? How can I improve and simplify this implementation?
Maybe I should just check rootObject.version value after unmarshalling, and manually set correct version of BaseClass/ExtendingClass. How should I get raw Xml precisely for BaseClass/ExtendingClass then?
Any help will be appreciated.
I used XmlAdapter to manually figure out xml format. It solves the problem, but results in hard to understand, dubious code.

REST Jersey server JAX-RS 500 Internal Server Error

I'm calling this method and getting a 500 back from it.
In the debugger I'm able to step though it all the way to the return statement at the end. No problem, r is populated as expected after Response.build() is called, the status says 200 OK. But that's not what ends up getting produced. I've even told eclipse to break on any Exception.
#GET
#Path("/getAllAppMessagesAsXML")
#Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public Response getAllAppMessagesXML(#QueryParam("applicationId") String applicationId){
ResponseList list = new ResponseList();
ArrayList<XmlMessageBean> xmlmessages = new ArrayList<>();
try {
List<AppMessage> messages = //Gets from a database
for(AppMessage m : messages){
XmlMessageBean xm = new XmlMessageBean();
xm.setId(m.getId());
xm.setApplicationId(m.getApplicationId());
xm.setMessageBody(m.getMessageBody());
xm.setMessageLevel(m.getMessageLevel());
xm.setMessageTitle(m.getMessageTitle());
xmlmessages.add(xm);
}
} catch (Exception e) {
logger.error("ERROR Failed to save Message AppMessageService.saveAppMessage()", e);
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
list.setList(xmlmessages);
Response r = null;
try{
r = Response.status(Response.Status.OK).entity(list).build();
}catch(Exception e){
e.printStackTrace();
}
return r;
}
XmlMessageBean.java
#XmlRootElement(name="AppMessage")
#XmlAccessorType(XmlAccessType.FIELD)
public class XmlMessageBean {
#XmlElement
private Long id;
#XmlElement
private String applicationId;
#XmlElement
private String messageTitle;
#XmlElement
private String messageBody;
#XmlElement
private String messageLevel;
public XmlMessageBean(){
}
//getters and setters
}
ResponseList.java
#XmlRootElement(name = "ResponseList")
public class ResponseList {
public ResponseList(){
}
#XmlElement(name="list")
private List<XmlMessageBean> list;
public List<XmlMessageBean> getList() {
return list;
}
public void setList(List<XmlMessageBean> list) {
this.list = list;
}
}
I've got this all running in a jersey.servlet.ServletContainer
I'm stumped. I can't figure out how to get it to produce any kind of error message other than a generic 500. I've tried setting up an exception mapper as some other posts have mentioned but this also isn't picking anything up.
IllegalAnnotationException: Class has two properties of the same name "list"
Look at your two model classes XmlMessageBean and ResponseList. Do you see any difference? The main difference (and the cause for the error), is the #XmlAccessorType(XmlAccessType.FIELD) annotation (or lack there of). JAXB by default will look for the public properties (JavaBean getters/setters). So that's one property. But then you define another property by using the #XmlElement annotation on the field. The reason it works for XmlMessageBean is that it overrides the default public property lookup by changing it to XmlAccessType.FIELD
So you can simply annotate the ResponseList with #XmlAccessorType(XmlAccessType.FIELD) and it should work. You could also simply get rid of all the #XmlElement annotations, and get rid of #XmlAccessorType(XmlAccessType.FIELD), and it will still work, as it will look up the JavaBean properties. Generally, for me I only use the #XmlElement annotations when I need to change the name of the property, and just put it on the getter, for example.
private String messageBody;
#XmlElement(name = "body")
public String getMessageBody(){
return messageBody;l
}
Other than that, I normally leave out the annotation, and also the #XmlAccessorType annotation, and just let it resolve to the default behavior.

JSON Jackson parse different keys into same field

I have a POJO which has a field:
public class Media {
private Asset asset;
}
Everything works perfectly when parsing a json response into this asset POJO. but however there is a slight difference with the key this asset comes with. It can either be:
#JsonProperty("cover_asset")
or
#JsonProperty("asset")
Is there a way to annotate the POJO to recognize this case and de-serialize into the same field. Its not possible for both of them to appear in the same response.
Well, as only deserialization is your concern, #JsonAlias introduced in 2.9 is perfect for this situation. You can do something like this:
#JsonAlias({"cover_asset", "asset"})
private Asset asset;
#JsonAlias docs:
Annotation that can be used to define one or more alternative names
for a property, accepted during deserialization as alternative to the
official name. Alias information is also exposed during POJO
introspection, but has no effect during serialization where primary
name is always used.
Note: Make sure you update all related dependencies(annotations, core, databind) if you are using them. Updating just annotations without others threw me runtime error.
More succinctly, I would suggest using two separate #JsonSetter annotations for this. Here's a working example. This means that your java class will only have one getter method to use for the property instead of two. You can also make the setter you don't want exposed to clients of Media private and treat one of the json keys in a special manner.
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.ObjectMapper;
#SuppressWarnings("unused")
public class Media {
private Asset asset;
#JsonGetter("asset")
public Asset getAsset() {
return asset;
}
#JsonSetter("asset")
public void setAsset(Asset asset) {
this.asset = asset;
}
#JsonSetter("cover_asset")
private void setMediaAsset(Asset asset) {
if (this.asset == null) {
setAsset(asset);
}
}
private static class Asset {
#JsonProperty("foo")
private String foo;
}
public static void main(String[] args) throws Exception {
String withAsset = "{'asset': {'foo':'bar'}}";
String withCoverAsset = "{'cover_asset': {'foo':'bar'}}";
ObjectMapper mapper = new ObjectMapper();
Media mediaFromAsset = mapper.readValue(withAsset.replace('\'','"'), Media.class);
Media mediaFromCoverAsset = mapper.readValue(withCoverAsset.replace('\'','"'), Media.class);
System.out.println(mediaFromAsset.asset.foo.equals(mediaFromCoverAsset.asset.foo));
}
}
Great answer By Vikas with JsonAlias.
Just adding that you can also benefit from both of the worlds (JsonProperty&Alias) [Since jackson 2.9]:
#JsonProperty("cover_asset")
#JsonAlias({"asset", "cover_asset","amazing_asset"})
private Asset asset;
Reference.
I'd propose to use getters/setters, for both property names, which are referring to the same POJO field.
public class Media {
private Asset asset;
#JsonProperty("cover_asset")
public Asset getCoverAsset() {
return asset;
}
public void setCoverAsset(Asset asset) {
this.asset= asset;
}
#JsonProperty("asset")
public Asset getAsset() {
return asset;
}
public void setAsset(Asset asset) {
this.asset= asset;
}
}
See also my answer to possible duplicate question:
Different names of JSON property during serialization and deserialization

Simple Xml - order of elements not preserved?‏

I am using SimpleXml 2.6.1 in my android app. Eventhough the documentation (http://simple.sourceforge.net/download/stream/doc/javadoc/index.html?org/simpleframework/xml/Order.html) says the order of the elements in the xml are same as the way they have defined in the class file, I am always getting the order to be random in the xml. If I add few more variables, the order of the elements again changes.
Adding #Order notation works, but since the class is complex with 100s of variables, I do not want to add order. Is this a known bug for android versions? It works fine in java console programs.
p.s: I opened the .class file disassembled and found the variables declared in the same order as java file, so I don't think it's a class file issue.
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Order;
#Order(elements = {"name", "isTrue"})
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
#Element
private String name;
#Element
private Boolean isTrue;
...
Since there is no answer, I'll try to save precious time to anyone who gets here.
I found no cause, and since I don't have time to analyze Simple libraries, I came up with a "workaroud". It's more of an advice, actually - don't use it for (marshaling)creating xml if you have a large xml definition and the order matters(a rule more than an exception). The order is mostly used for marshaling anyway so just save yourself some time and do it manually.
The template:
<document>
<name>$NAME$</name>
<isTrue>$IS_TRUE$</isTrue>
</document>
The class:
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
/**
* User: ksaric
*/
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
private String name;
private Boolean isTrue;
public SimpleXml() {
}
public Boolean getTrue() {
return isTrue;
}
public void setTrue(Boolean aTrue) {
isTrue = aTrue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
String template = null;
try {
template = getTemplate();
} catch (IOException e) {
e.printStackTrace();
}
/* GUAVA - checkNotNull() */
if (null == template) return null;
template = template.replace(NAME, getName());
/* OR CONVERT IN THE GETTER METHOD */
template = template.replace(IS_TRUE, getTrue().toString());
return template;
}
/* SINGLETON? Performance(IO) loss... */
public String getTemplate() throws IOException {
InputStream templateStream = getClass().getResourceAsStream("/template.xml");
/* APACHE IO COMMONS */
/*
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
*/
final String stringTemplate = IOUtils.toString(templateStream);
return stringTemplate;
}
}
The test:
import org.junit.Test;
import static junit.framework.Assert.*;
/**
* User: ksaric
*/
public class SimpleXmlTest {
#Test
public void test() throws Exception {
//Before
/* Use standard instantiation, factory method recommended for immutability */
SimpleXml simpleXml = new SimpleXml();
simpleXml.setName("This is a name");
simpleXml.setTrue(false);
//When
String result = simpleXml.toString();
//Then
assertNotNull(result);
System.out.println(result);
}
}
Not really an answer, but save yourself some time and don't use Simple(which is a great library) on Android...
Simple Xml doesn't preserve Order on Android. Based on pfh's answer, here is my recommendation:
I would prefer to use JAXB in the case where you want the order to be preserved than manual string/template parsing. JAXB is slightly complex to use than SimpleXml but comes with similar set of annotations based xml serialization and deserialization.

Categories