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
Related
I have data structures generated by jaxb. Parts of the structures are basically identical but they are in different namespaces and therefore the generated Java types are different.
I need to transfer data between these structures. In the project ModelMapper is used for mapping so I am expected to use that.
My problem is that ModelMapper can't map the lists generated for 'maxOccurs="unbounded"' elements.
Let's say I have the following schema:
<xs:complexType name="CityData">
<xs:sequence>
<xs:element name="districtData" type="DistrictData" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DistrictData">
<xs:sequence>
<xs:element name="population" type="xs:int" nillable="false" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
I have this schema in namespacea and in namespaceb so
Jaxb generates the following types into package namespacea and into package namespaceb:
public class CityData {
#XmlElement(required = true)
protected List<DistrictData> districtData;
//... jaxb explanation why there's no setter
public List<DistrictData> getDistrictData() {
if (districtData == null) {
districtData = new ArrayList<DistrictData>();
}
return this.districtData;
}
}
public class DistrictData {
protected int population;
public int getPopulation() {
return population;
}
public void setPopulation(int value) {
this.population = value;
}
}
Now If I create a source CityData from package namespacea and ask modelmapper to map it to a destination CityData in namespaceb then the data is not mapped:
CityData cityData = new CityData();
DistrictData districtData = new DistrictData();
districtData.setPopulation(1234);
cityData.getDistrictData().add(districtData);
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
namespaceb.CityData dest = modelMapper.map(cityData, namespaceb.CityData.class);
System.out.println("dest.districtData: " + dest.getDistrictData());
result is:
dest.districtData: []
In other words, districtData is not copied to destination.
I understand that ModelMapper does not find a setter for districtData and therefore not map it. I read that one can reconfigure Jaxb to generate setters for list properties, but the jaxb object generation is not in my hand in the project.
So I would like to find out if there is a nice solution for the mapping with ModelMapper, or maybe with other mapper library in these cases.
I've created a mini project: https://github.com/riskop/ModelMapperJaxb
I think that you just need to enable FieldMatching and set the access level of the fields to match to handle the missing setter. Check this configuration:
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT)
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(AccessLevel.PROTECTED);
Javadoc:
setFieldAccessEnabled
Sets whether field matching should be enabled. When true, mapping may take place between accessible fields. Default is false.
setFieldAccessLevel
Indicates that fields should be eligible for matching at the given accessLevel.
Note: Field access is only used when field matching is enabled.
I had a rough idea of a clumsy workaround with ModelMapper.Converter facility before reading pirho's answer. I think that pirho's answer is better (accepted) but for the record, below is the Converter workaround. This is basically manually defining the conversion for the substructures where there is no setter:
CountryData countryData = new CountryData();
CityData cityData = new CityData();
DistrictData districtData = new DistrictData();
districtData.setPopulation(1234);
cityData.getDistrictData().add(districtData);
countryData.getCityData().add(cityData);
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.addConverter(new Converter<CountryData, namespaceb.CountryData>() {
#Override
public namespaceb.CountryData convert(MappingContext<CountryData, namespaceb.CountryData> context) {
namespaceb.CountryData result = new namespaceb.CountryData();
if(context.getSource() != null) {
for(CityData cityData : context.getSource().getCityData()) {
namespaceb.CityData mapped = modelMapper.map(cityData, namespaceb.CityData.class);
result.getCityData().add(mapped);
}
}
return result;
}
});
modelMapper.addConverter(new Converter<CityData, namespaceb.CityData>() {
#Override
public namespaceb.CityData convert(MappingContext<CityData, namespaceb.CityData> context) {
namespaceb.CityData result = new namespaceb.CityData();
if(context.getSource() != null) {
for(DistrictData districtData : context.getSource().getDistrictData()) {
namespaceb.DistrictData mapped = modelMapper.map(districtData, namespaceb.DistrictData.class);
result.getDistrictData().add(mapped);
}
}
return result;
}
});
namespaceb.CountryData destCountryData = modelMapper.map(countryData, namespaceb.CountryData.class);
assertEquals(1, destCountryData.getCityData().size());
namespaceb.CityData destCityData = destCountryData.getCityData().get(0);
assertEquals(1, destCityData.getDistrictData().size());
namespaceb.DistrictData destDistrictData = destCityData.getDistrictData().get(0);
assertEquals(1234, destDistrictData.getPopulation());
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 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.
I have a very simple class that I want to transmit as XML. The accepted XML format is a list of fields made of name attribute and sequence of string values. Can I annotate my class so that I can:
Use class name as attribute "type" of xml entity
Use class fields names as attribute "name" in xml sequence of Fields ?
What would be the recommended way? The example class in question:
class MyEntityType {
public List<String> myField1; // = {"A", "B"}
public List<String> myField2; // = {"C", "D"}
}
in xml format:
<Entity type="MyEntityType">
<Fields>
<Field name="myField1">
<Value>A</Value>
<Value>B</Value
</Field>
<Field name="myField2">
<Value>C</Value>
<Value>D</Value>
</Field>
</Fields>
</Entity>
What I would like to avoid is having to declare a Field class, and put a list of them in my object, because I want to enforce a particular set of required fields.
class MyEntityType {
public static class Field {
String name;
String values
}
public List<Fields> fields;
}
For completeness these are supported schemas:
<xs:complexType name="EntityComplexType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:sequence>
<xs:element name="Fields" type="FieldsComplexType" />
</xs:sequence>
<xs:attribute name="Type" type="xs:string" use="required" />
</xs:complexType>
<xs:complexType name="FieldsComplexType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:sequence>
<xs:element name="Field" type="FieldComplexType" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="FieldComplexType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:sequence>
<xs:element name="Value" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Alias" type="xs:string" use="optional" />
<xs:attribute name="ReferenceValue" type="xs:string" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="Name" use="required" />
</xs:complexType>
I suppose that you realize that what you want is a significant deviation from XML's standard procedure, ignoring the concepts underlying the design of this data format, and the Java Architecture for XML Binding as well. Thus, class fields are meant to be represented by element names. Also, using an XML structure that does not reflect the structure of the data (class MyEntityType) or, vice versa, using a class that does not correspond to the structure of the data creates a gap that no out-of-the-box tool can handle. There are neither annotations nor external bindings to achieve this completely different structure.
There is, of course, the JAXB feature of adapter, which helps to replace one Java type by another, but here one has to replace the entire Java structure before it can be marshalled into XML.
Your XML schema (with a small correction) generates the required Java classes EntityComplexType, FieldsComplexType and FieldComplexType (which I don't paste here).
A Java class EntityAdapter can be written in the spirit of an adapter:
public class EntityAdapter {
public MyEntityType unmarshal(EntityComplexType v){
// ...
return new MyEntityType();
}
public EntityComplexType marshal(MyEntityType v){
EntityComplexType ect = new EntityComplexType();
ect.setType( v.getClass().getSimpleName() );
FieldsComplexType fct = new FieldsComplexType();
ect.setFields( fct );
FieldComplexType field1 = new FieldComplexType();
fct.getField().add( field1 );
field1.setName( "myField1" );
for( String s: v.getMyField1() ){
field1.getValue().add( s );
}
FieldComplexType field2 = new FieldComplexType();
fct.getField().add( field2 );
field2.setName( "myField2" );
for( String s: v.getMyField2() ){
field2.getValue().add( s );
}
return ect;
}
}
And marshalling proceeds as usual, with the addition of transforming the MyEntityType object to an EntityComplexType object:
MyEntityType met = ...;
EntityAdapter adapter = new EntityAdapter();
EntityComplexType ect = adapter.marshal( met );
ObjectFactory of = new ObjectFactory();
JAXBElement<EntityComplexType> jbe = of.createEntityComplexType( ect );
JAXBContext jc = JAXBContext.newInstance( PACKAGE );
Marshaller m = jc.createMarshaller();
m.marshal( jbe, ... );
I want to generate this XML:
<myElement myAttribute="whateverstring" xsi:type="hardPart"/>
I have this XSD:
<xsd:element name="myElement">
<xsd:complexType>
<xsd:attribute name="myAttribute" type="xsd:boolean" />
<!-- need to add the xsi:attribue here -->
</xsd:complexType>
</xsd:element>
How exactly can I accomplish this in my XSD (FYI: I am using it to marshall objects into XML in Java, using JiBX).
Assuming when you say xsi:type, you mean the "type" attribute from the "http://www.w3.org/2001/XMLSchema-instance" namespace. It is not something that you add to your XML schema, it is a reserved means of qualifying an element (similar to a cast in Java).
For the following to be valid:
<myElement myAttribute="whateverstring" xsi:type="hardPart"/>
You would need to have an XML schema like:
<xsd:element name="myElement" type="myElementType"/>
<xsd:complexType name="myElementType">
<xsd:attribute name="myAttribute" type="xsd:boolean" />
</xsd:complexType>
<xsd:complexType name="hardPart">
<xsd:complexContent>
<xsd:extension base="myElementType">
...
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Then when your XML binding solution marshals an object corresponding to the type "hardPart" it may represent it as:
<myElement myAttribute="whateverstring" xsi:type="hardPart"/>
Since myElement corresponds to the super type "myElementType", and needs to be qualified with xsi:type="hardPart" to represent that the content actually corresponds to the subtype "hardPart".
JAXB Example
MyElementType
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class MyElementType {
private String myAttribute;
#XmlAttribute
public void setMyAttribute(String myAttribute) {
this.myAttribute = myAttribute;
}
public String getMyAttribute() {
return myAttribute;
}
}
HardPart
public class HardPart extends MyElementType {
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(HardPart.class, MyElementType.class);
HardPart hardPart = new HardPart();
hardPart.setMyAttribute("whateverstring");
JAXBElement<MyElementType> jaxbElement = new JAXBElement(new QName("myElement"), MyElementType.class, hardPart);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(jaxbElement, System.out);
}
}