Autogenerate a field value during JAXB unmarshalling - jaxb

Say, I have a XML as below:
<MyBooks>
<book>
<author>some author</author>
<name>Book A</name>
</book>
<book>
<author>some other author</author>
<name>Book B</name>
</book>
</MyBooks>
....and I want a class like this to represent each book:
class Book{
String id;
String author;
String name;
}
While using JAXB, can we set an auto-generated unique id to the "id" field for each book object...during un-marshalling itself? (I don't want the supplier of the XML to bear the pain of adding an "id" for each book in the XML).

Implement this method in your Book class. Use it to create the id you want. (It is not part of an interface, just match the signature)
//This method is called after all the properties (except IDREF) are unmarshalled for this object,
//but before this object is set to the parent object.
void afterUnmarshal(Unmarshaller, Object parent);

Related

Xpages ObjectData Custom Control: Mixing compositeData with text

Would like to make a custom control for my DataObjects which would have two properties, the javaClass and the javaModel.
So if I have a java class names acme.com.model.Person, the javaClass property would be acme.com.model.Person and the javaModel property would be Person.
I started to build my custom control but only got a few things put in before I ran into syntactical problems.
The real problem is createObject. I don't understand how I can replace the presently hard-coded "Person" in the createObject with my compositeData values. Is this even possible?
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.data>
<xe:objectData
saveObject="#{javascript:compositeData.javaModel + 'save()'}"
var="${javascript:compositeData.javaModel}">
<xe:this.createObject><![CDATA[#{javascript:var Person = new com.scoular.model.Person();
var unid = context.getUrlParameter("key")
if (unid != "") {
Person.loadByUnid(unid);
viewScope.put("readOnly","Yes");
} else {
Person.create();
viewScope.put("readOnly","No");
}
return Person;}]]></xe:this.createObject>
</xe:objectData>
</xp:this.data>
</xp:view>
Just as you might try to de-couple your business logic from your "UI" (XPages markup), you could move your "create" code to a constructor method to the Person class, one which:
calls out to check for the URL Parameter of "key"
sets the key to a property (making this a bit more bean-like, optional, but likely a good idea)
invokes the loadByUnid(String) method
and puts the appropriate readOnly value into viewScope
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
// constructor method
public Person(){
Map<String, Object> reqParm = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String unid = (String) reqParm.get("key");
Map<String, Object> vwScope = ExtLibUtil.getViewScope();
if (unid != "") {
this.loadByUnid(unid);
vwScope.put("readOnly","Yes");
} else {
this.create();
viewScope.put("readOnly","No");
}
}
//...
}
Then your createObject block would look more like:
<xe:this.createObject><![CDATA[#{javascript:return new com.scoular.model.Person();}]]></xe:this.createObject>
This should shift enough of the specifics off of the markup layer so as to be far more reusable across specific classes, provided each self-construct like that.
As for the general mixing compositeData with text, you should be passing an object reference so for the example of the save method above, I think you should be able to access it more via compositeData.javaModel.save();, provided the save method exists in the object referenced by compositeData.javaModel. I don't think appending the string of a method will work, but I can't say I've tried it that way.

JAXB and MOXy xml and json marshalling of generic list in jersey

I'm making a little restful client in Jersey, and i have come into a little trouble with supporting both XML and JSON marshaling.
The specific problem is about marshaling an object that holds a few properties, with a generic list included.
I have the following class annotated as followed:
#XmlRootElement
public class Block<E> {
private String headerText;
private List<E> elements;
public Block() {
}
#XmlElement
public String getHeaderText() {
return headerText;
}
#XmlElementWrapper(name = "elements")
#XmlElements({
#XmlElement(name = "element", type=Foo.class),
#XmlElement(name = "element", type=Bar.class)
})
public List<E> getElements() {
return elements;
}
}
The XML comes out fine:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<block>
<elements>
<element>
<id>1</id>
<title>Title01</title>
</element>
<element>
<id>2</id>>
<title>Title02</title>
</element>
</elements>
<headerText>FooBarHeader</headerText>
</block>
but the JSON is formatted like this
{
-elements : {
-element: [
- {
id : 1
title : "Title01"
}
- {
id : 2
title : "Title02"
}
]
}
headerText : "HeaderText"
}
I would of course be interested in not having the "element" property in my JSON output, and only have elements: [{}...{}]
I have already setup a ContextResolver that creates a MOXyConfig with properties for JSON_WRAPPER_AS_ARRAY_NAME - and this works fine for fx. A List<String> where I only have to declare the property with #XmlElement instead of #XmlElements.
Anyone who know of a solution of this problem?
JSON_WRAPPER_AS_ARRAY_NAME Property
The effect of the JSON_WRAPPER_AS_ARRAY_NAME property (see: http://blog.bdoughan.com/2013/03/binding-to-json-xml-handling-collections.html) depends on whether the item names are significant or not,
Insignificant Item Names
Below we know that each item in the collection is an instance of Foo or an instance of a subclass of Foo.
#XmlElementWrapper(name = "elements")
#XmlElement(name = "element")
public List<E> getElements() {
return elements;
}
Significant Item Names
In the case of #XmlElements the item name is significant since it tells us which class we need to instantiate on the unmarshal and can not be excluded.
#XmlElementWrapper(name = "elements")
#XmlElements({
#XmlElement(name = "foo", type=Foo.class),
#XmlElement(name = "bar", type=Bar.class)
})
public List<E> getElements() {
return elements;
}
What You Can Do
You can use MOXy's externmal mapping document to override the mapping for the elements property to the following:
#XmlElementWrapper(name = "elements")
#XmlElement(name = "foo")
public List<E> getElements() {
return elements;
}
Then the JAXBContext for XML will be based on the annotations, and the JAXBContext for JSON will be based on the annotatons and external mapping document.
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2011/09/mapping-objects-to-multiple-xml-schemas.html
Try registering another JSON Entity Provider, which will disable automatically Moxy for JSON marshalling (see the ref). If you enable e.g Jackson, you will have its own annotations to control how everything is marshalled.

XML element with two attributes and simple element using JAXB

I would like to un marshall using JAXB. I am facing issue while unmarshalling for the following use case.
Please suggest how to generate below xml using JAXB.
<Source type="system" gender="male">
<Description>He is a man</Description>
</Source>
You can do the following. The #XmlAttribute annotation is used to mark something an XML attribute. By default everything is treated as an element, if you need the name different from the default then you can use the #XmlElement annotation to specify this.
#XmlRootElement(name="Source")
#XmlAccessorType(XmlAccessType.FIELD)
public class Source {
#XmlAttribute
private String type;
#XmlAttribute
private String gender;
#XmlElement(name="Description")
private String description;
}

Different name for a generated XSD attribute from that of the java object

Is it possible to generate a XSD from Java object with a name different that of the class or its attribute ? Say I have a class having an attribute "currency" and in the generated xsd i want to rename it as "currencyType"?
Many of the JAXB (JSR-222) annotations (i.e. #XmlElement, #XmlAttribute, #XmlType, etc) enable you to specify a name to override the default one derived from the class or property name.
#XmlElement(name="currencyType")
public String getCurrency() {
return currency;
}

XmlSeeAlso and XmlRootElement names?

In the reference JAXB implementation is there anyway to get XmlSeeAlso to use the name= value from XmlRootElement?
The effect I want is for the type attribute to use the name= value rather than actual class name from XmlSeeAlso.
Is this possible is some other JAXB implementation?
Small example:
#XmlRootElement(name="some_item")
public class SomeItem{...}
#XmlSeeAlso({SomeItem.class})
public class Resource {...}
XML:
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item">
...
</resource>
Possible without a lot of effort?
About #XmlSeeAlso
The purpose of the #XmlSeeAlso annotation is just to let your JAXB (JSR-222) implementation know that when it is processing the metadata for Resource that it should also process the metadata for the SomeItem class. Some people mistakenly believe that it is related to mapping inheritance since that is the use case it is most often used with. Since the subclasses of a class can not be determined using Java reflection, #XmlSeeAlso is used to let the JAXB implementation know that mappings for the subclasses should also be created.
Below is an example of how you could support your use case:
Resource
The complex type name corresponding to a Java class is supplied via the #XmlType annotation.
package forum12288631;
import javax.xml.bind.annotation.XmlType;
#XmlType(name="some_item")
public class Resource {
}
Demo
The root element name can come from the #XmlRootElement annotation or can be supplied via an instance of JAXBElement. We will create an instance of JAXBElement and indicate that it is holding onto an instance of Object. When marshalled this will for the xsi:type attribute to be included in the output.
package forum12288631;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Resource.class);
Resource resource = new Resource();
JAXBElement<Object> jaxbElement = new JAXBElement<Object>(QName.valueOf("resource"), Object.class, resource);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}
Output
The resulting XML has the root element supplied by the JAXBElement and the value of the xsi:type attribute comes from the #XmlType annotation on Resource.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item"/>

Resources