I want to convert uid attribute's value in MyJaxbModel class to uppercase during UnMarshalling. I did write UpperCaseAdapter that does the work for me. However with this approach, application performance is deteriorated to unacceptable level (as there are thousands of XML files unmarshalled to MyJaxbModel). I cannot use String.toUppperCase() in getter /setter as these JAXB models are auto generated from XSD and I dont want to tweak them.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "myJaxbModel")
public class MyJaxbModel
{
protected String name;
#XmlJavaTypeAdapter(UpperCaseAdapter.class)
protected String uid;
// getters and setters
}
public class UpperCaseAdapter extends XmlAdapter<String, String>
{
#Override
public String unmarshal( String value ) throws Exception
{
return value.toUpperCase();
}
#Override
public String marshal( String value ) throws Exception
{
return value;
}
}
<!--My XSD makes use of below xjc:javaType definition to auto-configure this-->
<xsd:simpleType name="uidType">
<xsd:annotation>
<xsd:appinfo>
<xjc:javaType name="java.lang.String"
adapter="jaxb.UpperCaseAdapter" />
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:string" />
</xsd:simpleType>
Expected Input:
<myJaxbModel name="abc" uid="xyz" />
Expected Output: myJaxbModel.toString() -> MyJaxbModel[name=abc, uid=XYZ]
Is there a better approach to achieve desired results?
Why didn't you simply parse it to upper case in the getUid() or while setting?
if (uid != null){
return uid.toUpperCase();
}
...
or
...
if (uid != null){
this.uir = uid.toUpperCase();
}
I think that is the easier and cleanest way to do it...
Related
I have an empty tag like this <tagName/>. When I unmarshalling it if this property is the type of long or float it is null. But if this property is the type of string, the property is tagName = '';. And after marshalling is <tagName></tagName>. How can I set empty tag name which is string java property to null while unmarshalling?
There are (at least) 2 ways to do this.
If the classes are yourself and not auto-generated from xsd or similar you can use an adapter.
For example a class Cart:
#XmlRootElement(name = "Cart")
#XmlAccessorType(XmlAccessType.FIELD)
public class Cart {
#XmlJavaTypeAdapter(EmptyTagAdapter.class)
protected String tagName;
}
can use an adapter like below:
public class EmptyTagAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String arg0) throws Exception {
return arg0;
}
#Override
public String unmarshal(String arg0) throws Exception {
if(arg0.isEmpty()) {
return null;
}
return arg0;
}
}
For an xml that looks like this:
<Cart>
<tagName/>
</Cart>
You would get the empty tagName as null.
If your classes are generated from an xsd you could mention that the field can be nillable.
For example as below:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.1">
<xs:element name="Cart">
<xs:complexType>
<xs:all>
<xs:element name="tagName" type="xs:string" nillable="true" />
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
and then you would need to have in your xml with the empty element xsi:nil="true" as this example:
<Cart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tagName/>
<tagName xsi:nil="true" />
</Cart>
It would have the same result, the value as null.
The use of the adapter is more to my liking but depends on your case. Hopefully one of the cases covers you.
I wrote a an attribute converter. I want to apply that in an entity. I'm following a purely XML approach so far.
I could not find an equivalent of #Convert in hbm notation.
An example would be appreciated.
When I search for this, understandably, Google returns lots of results about tools/methods on "Auto Converting hbm files to entities vice versa".
Edit:
Now I'm suspecting if there is an option in hbm file, given that this is JPA annotation.
The doc of #Convert says:
The Convert annotation is used to specify the conversion of a Basic
field or property. It is not necessary to use the Basic annotation or
corresponding XML element to specify the basic type.
I'm not entirely sure what it means. Is mixing annotation and XML a way to go in this case?
I've tried this:
public class Person {
//this is enum
private Ethnicity ethnicity;
//.....
}
public enum Ethnicity{
INDIAN("IND"),
PERSIAN("PER")
//...constructors and value field.
public String value(){
return this.value;
}
public Ethnicity fromValue(String value){
//logic for conversion
}
}
Converter:
#Converter
public class EthnicityConverter implements AttributeConverter<Ethnicity,String> {
#Override
public Ethnicity convertToEntityAttribute(String attribute) {
if ( attribute == null ) {
return null;
}
return Ethnicity.fromValue( attribute );
}
#Override
public String convertToDatabaseColumn(Ethnicity dbData) {
if ( dbData == null ) {
return null;
}
return dbData.value();
}
}
HBM File:
//....other columns
<property name="ethnicity">
<column name="ethnicity"/>
<type name="EthnicityConverter"/>
</property>
//....other columns
Edit: Corrected the converter code.
The answer from Sarvana is close - you do in fact use the type XML attribute. However, type is used to name a Hibernate Type. However there is a convention to name, instead, an AttributeConverter - simply apply the prefix converted:: to your AttributeConverter FQN. E.g.,
<property name="ethnicity">
<column name="ethnicity"/>
<type name="converted::EthnicityConverter"/>
</property>
The other option is to auto-apply the converter:
#Converter( autoApply=true)
public class EthnicityConverter implements AttributeConverter<Ethnicity,String> {
...
}
Given the converter above, so long as Hibernate knows about it, Hibernate will apply that to any attribute of type Ethnicity.
HTH
type is the equivalent xml attribute for Convert annotation.
Below is to convert to Y/N in DB and Boolean in entity.
<property name="status" column="book_status" type="yes_no" not-null="true"/>
Just replace yes_no with your custom converter class
Please see my answer at
https://stackoverflow.com/a/37914271/3344829
Official documentation
https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch06.html
Update
<property name="ethnicity" column="ethnicity" type="com.example.EthnicityConverter"/>
Update
#Converter
public class EthnicityConverter implements AttributeConverter<Ethnicity, String> {
#Override
public String convertToDatabaseColumn(Ethnicity attribute) {
// TODO return String value of enum
}
#Override
public Ethnicity convertToEntityAttribute(String dbData) {
// TODO return resolved enum from string
}
}
I too had to face this issue and i was able to resolve it.
Refer : Hibernate 5 Documentation Example 33. HBM mapping for AttributeConverter
We have to use exact class with package.
Ex :
<property name="ethnicity" column="ethnicity" type="converted::com.example.EthnicityConverter"/>
I've got the following xsd tag:
<xs:complexType name="documentation">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="language" use="required"/>
</xs:extension>
</xs:simpleContent>
this generates (with jax-b):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "documentation", propOrder = {
"value"
})
public class Documentation {
#XmlValue
protected String value;
#XmlAttribute(name = "language", required = true)
protected String language;
And I want some output like:
<documentation language="NL">SomeValue</documentation>
but Xstream generates:
<documentation language="NL">
<value>SomeValue</value>
</documentation>
how could I remove the value tags? I don't want them..
Code to generate the xml tags (this is just a snippet..):
private void createDocumentation(Description description, String docNL) {
List<Documentation> documentations = description.getDocumentation();
Documentation documentationNL = new Documentation();
documentationNL.setLanguage("NL");
documentationNL.setValue(docNL);
documentations.add(documentationNL);
}
private void createXmlFile(Description description) {
XStream xstream = new XStream(new DomDriver());
xstream.alias("description", Description.class);
xstream.alias("documentation", Documentation.class);
xstream.addImplicitCollection(Description.class, "documentation");
xstream.useAttributeFor(Documentation.class, "language");
String xml = xstream.toXML(description);
}
XStream provides a standard converter implementation called ToAttributedValueConverter that you can wire in for any simple-content-plus-attributes type like this:
#XStreamConverter(value = ToAttributedValueConverter.class, strings = { "value" })
public class Documentation {
protected String value;
protected String language;
}
The strings annotation element names the property that corresponds to the element content, all other properties will become attributes. If you want to declare the converter using xstream.registerConverter instead of using XStream annotations then you use
xstream.registerConverter(new ToAttributedValueConverter(Documentation.class,
xstream.getMapper(), xstream.getReflectionProvider(), xstream.getConverterLookup(),
"value"));
(the Mapper, ReflectionProvider and ConverterLookup objects get supplied to the converter automatically when you register it using annotations, but must be provided explicitly for registerConverter).
One option is to create a custom converter for your Documentation object.
Take a look at the XStream Converter tutorial
EDIT TS:
adding:
xstream.registerConverter(new DocumentationConverter());
and
public class DocumentationConverter implements Converter {
public boolean canConvert(Class clazz) {
return clazz.equals(Documentation.class);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Documentation documentation = (Documentation) value;
writer.addAttribute("language", documentation.getLanguage());
writer.setValue(documentation.getValue());
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Documentation documentation = new Documentation();
reader.moveDown();
documentation.setLanguage(reader.getAttribute("language"));
documentation.setValue(reader.getValue());
reader.moveUp();
return documentation;
}
}
did the job
I'm trying to convert this XML to Java object and then updating key and value and then save it to XML.I can convert simple XML but this one has two attribute which is the same.
Can anybody help me to represent this xml in java class as Configuration.java?
XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="mode" value="1"/>
<add key="type" value="shs"/>
</appSettings>
</configuration>
Configuration.java
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Configuration {
String appSettings;
String add;
String key;
String value;
public String getAppSettings() { return appSettings; }
#XmlElement
public void setAppSettings(String appSettings) { this.appSettings = appSettings;}
public String getAdd() { return add; }
#XmlElement
public void setAdd(String add) { this.add = add; }
public String getKey() { return key; }
#XmlAttribute
public void setKey(String key) { this.key = key; }
public String getValue() { return value; }
#XmlAttribute
public void setValue(String value) { this.value = value; }
}
I suggest to:
specify the XSD
generate the JAXB classes using the following Maven plugin: http://java.net/projects/maven-jaxb2-plugin/pages/Home
Use JAXB if you want fine control over the XML to POJO creation. But you will have to specify the structure of your XML in an XSD first and let JAXB generate the Java classes for you.
Another way is to use XStream.
XStream xstream = new XStream();
Configuration config= (Configuration)xstream.fromXML(xml);
But you might have to update your Configuration class to use List for the add nodes as Kuldeep Jain said in his answer.
Edit: Take a look at the 2-minute XStream tutorial also - http://x-stream.github.io/tutorial.html
I think you will have to have a List for the follwing add nodes:
<add key="mode" value="1"/>
<add key="type" value="shs"/>
EDIT:
You may have a look at JAXB article for help.
I am using Jaxb2Marshaller to marshal Java beans through spring #ResponseBody annotation. For JSON marshaling was working fine. But for xml I was continuously getting HTTP 406 response. Little bit digging in Jaxb2Marshaller class reveals it checks for #XmlRootElement for bounded classes (see below snippet).
While generating java code from xsd my pojo does not contain #XmlRootElement and proper message converter was not identified by AnnotationMethodHandlerAdapter and finally result in 406.
Instead of auto generating java code from xsd I have created my own pojo class and used #XmlRootElement. Then marshaling works fine.
I want to understand why it is important of having #XmlRootElement check for bounded classes. Or any way to specify element as #XmlRootElement in xsd.
Code snippet from Jaxb2Marshaller:
public boolean supports(Class clazz) {
return supportsInternal(clazz, true);
}
private boolean supportsInternal(Class<?> clazz, boolean checkForXmlRootElement) {
if (checkForXmlRootElement && clazz.getAnnotation(XmlRootElement.class) == null) {
return false;
}
if (clazz.getAnnotation(XmlType.class) == null) {
return false;
}
if (StringUtils.hasLength(getContextPath())) {
String className = ClassUtils.getQualifiedName(clazz);
int lastDotIndex = className.lastIndexOf('.');
if (lastDotIndex == -1) {
return false;
}
String packageName = className.substring(0, lastDotIndex);
String[] contextPaths = StringUtils.tokenizeToStringArray(getContextPath(), ":");
for (String contextPath : contextPaths) {
if (contextPath.equals(packageName)) {
return true;
}
}
return false;
}
else if (!ObjectUtils.isEmpty(classesToBeBound)) {
return Arrays.asList(classesToBeBound).contains(clazz);
}
return false;
}
Edit:
Blaise answer helped me solve #XmlRootElement problem. But still if someone has any information about why check for XmlRootElement is required, will be a good info.
Why the #XmlRootElement Annotation is Checked For
Spring requires a root element when marshalling the object to XML. JAXB provides two mechanisms to do this:
The #XmlRootElement annotation
Wrapping the root object in an instance of JAXBElement.
Since the object is not wrapped in a JAXBElement Spring is ensuring that the other condition is met.
How Generate an #XmlRootElement
JAXB will generate an #XmlRootElement annotation for all global elements in an XML schema. The following will cause an #XmlElement:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="foo">
<xsd:complexType>
...
</xsd:complextType>
</xsd:element>
</xsd:schema>
When #XmlRootElement is not Generated
An #XmlRootElement annotation will not be generated for global types.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="foo" type="foo"/>
<xsd:complexType name="foo">
...
</xsd:complexType>
</xsd:schema>
Instead the global element(s) associated with the global types are captured in the ObjectFactory class (annotated with #XmlRegistry) in the form of #XmlElementDecl annotations. These annotations
package generated;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _Foo_QNAME = new QName("", "foo");
public Foo createFoo() {
return new Foo();
}
#XmlElementDecl(namespace = "", name = "foo")
public JAXBElement<Foo> createFoo(Foo value) {
return new JAXBElement<Foo>(_Foo_QNAME, Foo.class, null, value);
}
}
The #XmlElementDecl annotation provides similar information as #XmlRootElement and could be used for unmarshal operations. JAX-RS implementations probably do not leverage #XmlElementDecl however since marshal operations would require the object to be wrapped in a JAXBElement object to provide the root element name/namespace.
it's an known issue:
https://jira.springsource.org/browse/SPR-7931
"Checking for #XmlRootElement annotation should be made optional in Jaxb2Marshaller"
You can use JaxbElement for classes that does not have #XmlRootElement annotation. #XmlRootElement annotation is placed only to non referenced top level objects if you are generating your code from xsd
Edit
See #Blaise Doughan answer.
#XmlRootElement will be placed only if there is no reference to that type in another type.