I have an XML schema:
<xsd:element name="Person">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="lat" type="xsd:double" minOccurs="0"/>
<xsd:element name="lon" type="xsd:double" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
And I have an XML message:
<Person>
<name>Fred</name>
</Person>
I use JAXB to auto-generate my classes (i.e. Person.java, etc).
So at run time I use JAXB to unmarshal the above XML message and get a Person object instance. When I do a p.getLat() or p.getLon() the return values are 0.0 even though the original XML didn't contain <lat> or <lon> elements.
What makes this worse is that 0.0, 0.0 is a valid latitude and longitude. It's rare for a person to be located there but that's beside the point!
An article on the IBM site suggested using an additional XML element as metadata to explicitly state whether the optional element exists or not. i.e.
<xsd:element name="hasLat" type="xsd:boolean"/>
<xsd:element name="hasLon" type="xsd:boolean"/>
So the XML message above would become:
<Person>
<name>Fred</name>
<hasLat>false</hasLat>
<hasLon>false</hasLon>
</Person>
This seems like an ugly hack. There must be a proper way with JAXB to check if the element existed so that I can trust the return value from my getLat(), getLon()?
I don't see that problem at all. For me xjc generates a Person class with the properties lat and lon with type Double.
If I unmarshall an XML file with no <lat> or <lon> elements, then the resulting Person objects has null values for those properties, as I'd expect.
I don't know how you get 0.0 anywhere.
My XML Schema:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/person">
<xsd:element name="Person">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="lat" type="xsd:double" minOccurs="0"/>
<xsd:element name="lon" type="xsd:double" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
My Test.java:
import com.example.person.Person;
import javax.xml.bind.JAXB;
import java.io.File;
public class Test {
public static void main(String[] args) {
Person p = JAXB.unmarshal(new File("foo.xml"), Person.class);
System.out.println(p.getName());
System.out.println(p.getLat());
System.out.println(p.getLon());
}
}
My foo.xml:
<Person>
<name>Fred</name>
<lat>1.0</lat>
</Person>
Output:
Fred
1.0
null
The most likely reason for getting 0.0 returned vs null is the use of the Double primitive type or Double object type. The Double primitive will default to 0.0 if the value is null, since null is not a valid value for primitive types. The Double object will allow you to assign a null value to these fields. A peak at your Person class will probably reveal this.
Related
I would like to generate client stubs from a wsdl with wsimport maven plugin , this is works well but with one issue : i have two object that should be linked togerther but afetr the generation is donne , it's not perfect
<types>
<xsd:schema elementFormDefault="qualified" targetNamespace="http://www.hello.com/ns/xsd/boba/restitution-restituerCarriereAvecValo.xsd">
<xsd:import namespace="http://www.hello.com/holla/infosRetour.xsd" schemaLocation="xsd/infosRetour.xsd"/>
<xsd:import namespace="http://www.hello.com/ns/wsdl/boba/messageRetourModuleValorisation.xsd" schemaLocation="xsd/messageRetourModuleValorisation.xsd"/>
<xsd:element name="messageIn" type="xsd:anyType"/>
<xsd:element name="messageOut">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="ir:infosRetour">
<xsd:choice>
<xsd:element name="messageRetour" type="xsd:anyType"/>
<xsd:element name="rejetControleSyntaxiqueSemantique" type="xsd:anyType"/>
<xsd:element name="rejetControleIdentification" type="xsd:anyType"/>
</xsd:choice>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
here my object messageOut sould have a field messageRetour of type messageRetour class, but when i see my class messageOut this field is set as Object and not the montioned class.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"messageRetour",
"rejetControleSyntaxiqueSemantique",
"rejetControleIdentification"
})
#XmlRootElement(name = "messageOut")
public class MessageOut
extends InfosRetour
{
protected Object messageRetour;
protected Object rejetControleSyntaxiqueSemantique;
protected Object rejetControleIdentification;
as you can see here here is the sub class generated , you can see that there is a class named messageRetour
HOW CAN I CHANGE THIS TYPE TO BE SET AS A CLASS AND NOT AS OBJECT ?
even when i chnage this line <xsd:element name="messageRetour" type="xsd:anyType"/> to <xsd:element name="messageRetour"/> i got the same issue always Object and not a class
You get Object because of xsd:anyType as type.
You should change the type of <xsd:element name="messageRetour" type="xsd:anyType"/> to the messageRetour type.
This type is probably provided by one of the schemas you import. I'm not sure about the specific name, will be probably MessageRetour or messageRetour or message-retour, something like that. So it will probably be something like:
<xsd:element name="messageRetour" type="ir:messageRetour"/>
If the type is provided by the other imported schema, you may need to declare a namespace prefix first (like xmlns:mrmv="http://www.hello.com/ns/wsdl/boba/messageRetourModuleValorisation.xsd") and then use mrmv:messageRetour as type.
I have an XML schema whose topmost element is Document
<xsd:element name="Document" type="Document"/>
It contains one element of type ZZ_Customer which is restriction of Customer.
Both of these elements contain children of the same name but with slightly different types.
<xsd:complexType name="Document">
<xsd:sequence>
<xsd:element name="CstmrCdtTrfInitn" type="ZZ_Customer"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ZZ_Customer">
<xsd:complexContent>
<xsd:restriction base="Customer">
<xsd:sequence>
<xsd:element name="GrpHdr" type="ZZ_Group"/>
<xsd:element name="PmtInf" type="ZZ_Payment" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Customer">
<xsd:sequence>
<xsd:element name="GrpHdr" type="Group"/>
<xsd:element name="PmtInf" type="Payment" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
When JAXB unmarshalls an XML file will it create an instance of ZZ_Customer or will it create an instance of Customer? Likewise when will it create an instance of Group or ZZ_Group?
I've noticed is that JAXB will instances of ZZ_* for certain parts of the XML but uses their base counter parts for other parts of the XML.
On what basis does it makes its decisions? It does not appear obvious which criteria that JAXB is using.
Unfortunately I have no control over the schema and its design.
Since Document has a property that corresponds to the sub-type.
The sub-type will be created when unmarshalling. This makes sense since in Java when a property is typed to the subclass you can't set an instance of the super class on it.
If the property had corresponded to the super type, by default JAXB would have unmarshalled it to an instance of the super type. The XML can contain the xsi:type attribute to specify that the subtype is being used.
<xsd:element name="loginResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="loginReturn" type="tns:test"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="test">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="1" name="tx" type="xsd:int"/>
<xsd:element minOccurs="0" maxOccurs="1" name="result" type="xsd:int"/>
<xsd:element minOccurs="0" maxOccurs="1" name="name_space" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
I just want to ask why is the type tns:test used? How can I get the tx, result, namespace values in complextype name="test", because that's the response should I get based on the api they given to me.
tns is the target namespace prefix, which should be defined at the top of your WSDL or XSD file (which includes test).
You didn't wrote how you do access the values, but I assume that your code is working in a different namespace, so that test cannot be indentified. Most likely there is a method which allows you to get values by element name and namespace. Note that in that case, the namespace isn't tns but rather the URL which is defined at top of source file.
If you're not familiar with namespaces: Each XML element is associated with a namespace, like a class in Java is part of a package. In XML there is no import statement, so you have to name an element by name and namespace. To keep the files readable you can define namespace prefixes (probably as an abbreviation).
After going through this page, I wrote the following code to parse an XSD file. However I only get the root element, and I am lost so as to how to get the nested elements inside it.
Code:
XMLSchemaLoader loader = new XMLSchemaLoader();
XSModel model = loader.loadURI(url.toURI().toString());
XSNamedMap map = model.getComponents(XSConstants.ELEMENT_DECLARATION); //returns the root component
if(map!=null ){
for (int j=0; j<map.getLength(); j++) {
String name = map.item(j).getName(); //returns 'country' correctly.
}
}
I am not posting the entire xsd, but this is the structure:
<xsd:element name="country">
<xsd:complexType>
<xsd:annotation>
<xsd:appinfo id="substring">No</xsd:appinfo>
</xsd:annotation>
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="states" minOccurs="1" maxOccurs="1" >
<xsd:complexType>
<xsd:annotation>
<xsd:appinfo id="substring">No</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="cities" minOccurs="1" maxOccurs="unbounded">
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string"></xsd:attribute>
</xsd:complexType>
</xsd:element>
I am looking to read all the elements, not just the base element, and not sure how to proceed. Thanks for the help.
I think your trying to iterate over the XSNamedMap incorrectly. The reason you are getting only the country element is because it is the root element. You will probably have to descend down into XSNamespaceItem and call getComponents to retrieve another set of XSNamedMap objects.
This will properly parse the XSD but you still have to traverse the tree.
Using this simplified XSD (simplified, but still verbose as all XSDs tend to be):
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="[redacted]">
<xsd:element name="Statement" type="BILLINGSTATEMENTTYPEType"/>
<xsd:complexType name="BILLINGSTATEMENTTYPEType">
<xsd:sequence>
<xsd:element name="AccountSection" type="ACCOUNTSECTIONTYPEType"/>
<xsd:element name="DataSection" type="DATASECTIONTYPEType"/>
<xsd:element name="Summary" type="SUMMARYTYPEType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ACCOUNTSECTIONTYPEType">
<xsd:sequence>
<xsd:element name="Foo" type="xsd:string" maxOccurs="unbounded" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DATASECTIONTYPEType">
<xsd:sequence>
<xsd:element name="Bar" type="xsd:string" maxOccurs="unbounded" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SUMMARYTYPEType">
<xsd:sequence>
<xsd:element name="Baz" type="xsd:string" maxOccurs="unbounded" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
I generated a JAR file (using the <xmlbean> Ant task from xmlbeans), and everything appears to look great, I get all the right types and whatnot. But when I have it parse this simplified document:
<Statement>
<AccountSection>
<Foo>bar</Foo>
</AccountSection>
<DataSection>
</DataSection>
<Summary>
</Summary>
</Statement>
Using this code:
public class XmlTest {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./data/test.xml");
FileInputStream xmlStream = new FileInputStream(xmlFile);
BILLINGSTATEMENTTYPEType statement = BILLINGSTATEMENTTYPEType.Factory.parse(xmlStream);
ACCOUNTSECTIONTYPEType acctSection = statement.getAccountSection();
System.out.println(statement.xmlText());
System.out.println("acctSection is null:" + (acctSection == null));
}
}
The acctSection (and any of the child sections I've tried) are always null, even though it is fully parsing the document.
Output:
<Statement>
<AccountSection>
<Foo>bar</Foo>
</AccountSection>
<DataSection>
</DataSection>
<Summary>
</Summary>
</Statement>
acctSection is null:true
Why is it null? Why are they all null? Did I improperly define something somewhere in my XSD? I've used xmlbeans before successfully and never had this issue, which is why I'm sure I'm missing something but I've been unable to find it.
I'm not an export in xmlbeans myself, but I noticed that you used the Factory of the complex type to parse the xml. Can you try to use StatementDocument.Factory instead?
My problem was solved by adding elementFormDefault="qualified" in the namespace of my .xsd file.