Expression accessing attribute in number using getter method - java

One of my client to integrate provide an XML with attribute name "_1", "_2" ... etc.
e.g.
<element _1="attr1" _2="attr2">
using JAXB to generate the class, the getter method of the attribute will be get1() and get2()
However in my JSP pages, using JSTL and EL, sure I cannot access the value through
${variable.1}
How can I access the value using EL correctly?

You could use an external binding file to rename the property generate by JAXB:
schema.xsd
Below is a sample XML schema based on your post:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org"
xmlns:tns="http://www.example.org"
elementFormDefault="qualified">
<element name="element1">
<complexType>
<attribute name="_1" type="string" />
<attribute name="_2" type="string" />
</complexType>
</element>
</schema>
binding.xml
An external binding file is used to customize how Java classes are generated from the XML schema. Below we'll use an external binding file to rename the generated properties.
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="schema.xsd">
<jaxb:bindings node="//xsd:attribute[#name='_1']">
<jaxb:property name="one"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:attribute[#name='_2']">
<jaxb:property name="two"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
XJC Call
Below is an example of how you reference the binding file when using the XJC tool.
xjc -b binding.xml schema.xsd
Element1
Below is what the generated class will look like:
package forum12259754;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "element1")
public class Element1 {
#XmlAttribute(name = "_1")
protected String one;
#XmlAttribute(name = "_2")
protected String two;
public String getOne() {
return one;
}
public void setOne(String value) {
this.one = value;
}
public String getTwo() {
return two;
}
public void setTwo(String value) {
this.two = value;
}
}

Use this notation:
${variable.["1"]}

Related

Unmarshalling empty xml element jaxb

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.

Java JAXB exporting hashmaps to xml

I am having an issue with exporting a custom hashmap function to xml. The layout of my project is as follows. The package adt contains the following classes:
class HashMap implements Map<K,V>
A custom hashmap implementation, pretty much identical to java.util.HashMap. (Note, same errors exist when I use java.util.HashMap)
class Database extends HashMap <String,Table>
class Table extends HashMap <Object, Row>
(Note: The key of type Object is always a string in my implementation)
class Row extends HashMap <String, Object>
(Object is of type String, boolean, or int)
I am trying to export a table to an xml file, and eventually import it again but I haven't gotten that far. Here is the function which I use to export it:
private Response xml(Table table, FileWriter file) {
JAXBContext jaxbContext;
Marshaller jaxbMarshaller;
try {
jaxbContext = JAXBContext.newInstance(HashMap.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(table, file);
}catch (JAXBException e) {
e.printStackTrace(System.out);
return new Response(false,"Export - JAXBException.",null);
}//catch
return new Response(true,"Export - Successfully wrote table to file.",null);
}//xml
Also note...
- I can export flawlessly using JsonObjectBuilder from javax.json.* to a json file
- It is required to use the javax.xml library
- I really don't know what I'm doing
It returns the code:
?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hashMap/>
The table which I am trying to export does have data which is not represented in the XML output. Right now, the HashMap class has #XmlRootElement at the top. That is the only XML annotation in any class.
I also receive a NullPointerException when I change this line
jaxbContext = JAXBContext.newInstance(HashMap.class);
to this:
jaxbContext = JAXBContext.newInstance(Table.class);
The exception is as follows:
Exception in thread "main" java.lang.NullPointerException
at com.sun.xml.internal.bind.v2.runtime.property.SingleMapNodeProperty.serializeBody(SingleMapNodeProperty.java:252)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:345)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:578)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:326)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:479)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at driver.DExport.xml(DExport.java:99)
at driver.DExport.execute(DExport.java:59)
at core.Server.interpret(Server.java:57)
at core.Console.prompt(Console.java:56)
at core.Console.main(Console.java:37)
After much trial and error, it so turns out that I needed to create a list of Row extends HashMap<String,Object> types. I guess the fact that there was nested hashmapping (Table extends Hashmap<Object,Row extends Hashmap<String,Object>> screwed it up. Now, my Row class contains #XmlRootElement, and I made my Tuple(known as Entry in java.util.Hashmap) static, with the get methods for key and val having the #XmlElement annotation. Now I must export in a loop. Pretty sure this is the wrong way to do it but it works(kinda)...
jaxbContext = JAXBContext.newInstance(Row.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
for (int i = 0; i<rows.size(); i++) {
jaxbMarshaller.marshal(rows.get(i), file);
}//for
and the output...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<row>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">str</key>
<val xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">goodbye</val>
</Tuple>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bool</key>
<val xsi:type="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">false</val>
</Tuple>
</row>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<row>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">str</key>
<val xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">hello</val>
</Tuple>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bool</key>
<val xsi:type="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">true</val>
</Tuple>
</row>

JAXB Bindings Globally Replace Type with Implementing Class

I am generating code from an XSD using JAXB. I have an external bindings file that defines the implementation type for one of the XSD types (using the class tag/implClass attribute). When I do this, the generated ObjectFactory is modified to return an instance of the specified implementing class. However, the generated classes still declare members with the generated underlying type. Is there some way to make all uses of the generated XSD type reference my implementation type instead?
As an example, assume "example.xsd" is as follows:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" ...>
<xsd:element name="Root">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Member" type="MemberType" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="MemberType" />
</xsd:schema>
Then, I have the following "bindings.xjb" file:
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
schemaLocation="example.xsd" node="/xsd:schema" version="2.1">
<bindings node="./xsd:complexType[#name='MemberType']">
<class implClass="myNamespace.Member" />
</bindings>
</bindings>
After running xjc to generate JAXB classes in the above scenario, I get an ObjectFactory class that includes the following method:
public MemberType createMemberType() {
return new Member();
}
So the factory is correctly generating an instance of my implementation for MemberType. However, the implementation of the generated Root class includes:
protected List<MemberType> member;
public List<MemberType> getMember() {
if (member == null) {
member = new ArrayList<MemberType>();
}
return this.member;
}
I would instead like the implementation to be:
protected List<Member> member;
public List<Member> getMember() {
if (member == null) {
member = new ArrayList<Member>();
}
return this.member;
}
Is there a way to specify that I want to replace MemberType with myNamespace.Member within "bingings.xjb"? I would expect the result of ObjectFactory.createMemberType to change as well due to this configuration. Thanks.
After much trial and error, I finally discovered a way to do this. The following is the working "bindings.xjb" file:
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
schemaLocation="example.xsd" node="/xsd:schema" version="2.1">
<bindings node="./xsd:complexType[#name='MemberType']">
<class implClass="myNamespace.Member" />
</bindings>
<bindings node="//xsd:element[#name='Member']">
<property name="Members">
<baseType name="myNamespace.Member" />
</property>
</bindings>
</bindings>
The generated ObjectFactory.createMemberType method is unchanged:
public MemberType createMemberType() {
return new Member();
}
However, the generated Root class now has the following implementation:
protected List<myNamespace.Member> members;
public List<myNamespace.Member> getMembers() {
if (members == null) {
members = new ArrayList<myNamespace.Member>();
}
return this.members;
}
I have verified that the resulting JAXB objects are both marshalled and unmarshalled correctly.

JAXBElementRef does not generate nillable="true"

for the request for my web service, I wanted to differ between requested null value and missing tag. In other words I needed the following element definition:
<xs:element minOccurs="0" name="minzeronil" nillable="true" type="xs:string"/>
I developed the web service code-first, so I defined the element using JAXBElementRef:
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "minzeronil", namespace = XmlNamespace.MY_SERVICE)
public JAXBElement<String> createMinzeronil(final String value) {
return new JAXBElement<String>(new QName(XmlNamespace.MY_SERVICE, "minzeronil"), String.class, value);
}
}
Now, I expected to see nillable = "true" in the definition of the element. Instead, I got:
<xs:element name="minzeronil" type="xs:string"/>
<xs:element ref="tns:minzeronil" minOccurs="0"/>
How can I generate nillable = "true" from my java code? ... and still use JAXBElement in my code and its methods like isNil() ...
UPDATE: I deploy the code on glassfish, so glassfish is the one that generates the wsdl and xsd.
use #XmlElement(nillable = true) in your Java Class
#XmlElement(nillable=true)
public String getAString() {
return AString;
}
Refer to this Stack Overflow question/answer

JAXB: an element with textual content and attributes, generating classes with XJC

Recently I have faced a problem which seems to be very common: how to represent an XML element with attributes and simple textual content, like this:
<elem attr="aval">elemval</elem>
using JAXB.
I've found many advices on how to do this, but every one of these advices involves manual editing of binding classes.
I have a set of schemas and I use XJC to convert these schemas to Java classes. However, it seems that it produces wrong code, i.e. it does not generate methods to set plain content, there are methods for setting attributes only.
Is it possible to fix this behavior of XJC? Extensive googling didn't help on this question.
Below is an XML schema that defines the XML structure for you use case.
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema"
xmlns:tns="http://www.example.org/schema" elementFormDefault="qualified">
<element name="elem">
<complexType>
<simpleContent>
<extension base="string">
<attribute name="attr" type="string" />
</extension>
</simpleContent>
</complexType>
</element>
</schema>
Generating a JAXB model from this XML schema will result in the following class:
package forum12859885;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"value"
})
#XmlRootElement(name = "elem")
public class Elem {
#XmlValue
protected String value;
#XmlAttribute(name = "attr")
protected String attr;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getAttr() {
return attr;
}
public void setAttr(String value) {
this.attr = value;
}
}

Categories