I have a JAXB generated class PersonType with the properties name, phone, address. In the XSD I define that "phone" minoccurs="1".
How can I test programatically(i.e. via reflection, etc), in Java code, if the property "phone" is required or not in the XSD?
Later edit:
The JAXB generator creates a class without any "required", etc. attributes. So there's no annotation on the class or it's fields to indicate if it's required or not. However, there's the #XmlElement annotation on the field specifying the XSD equivalent element. So I guess the only solution is to inspect this annotation and then make an XPath or something against the XSD?
Thank you!
The #XmlElement annotation has a required property (false by default) that is used to indicate that an XML element is required (minOccurs > 0). You could use the java.lang.reflect APIs to get this value from the class.
XML Schema
In the XML schema below the foo element is required and the bar element is not.
<?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">
<complexType name="root">
<sequence>
<element name="foo" type="string"/>
<element name="bar" type="string" minOccurs="0"/>
</sequence>
</complexType>
</schema>
Generated Class
Below is the class that was generated from the XML schema. We see that the foo field is annotated with #XmlElement(required=true) anf the bar field is not.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "root", propOrder = {
"foo",
"bar"
})
public class Root {
#XmlElement(required = true)
protected String foo;
protected String bar;
}
Related
I ran SvcUtil.exe against XSD file to generate class. Then tried to create Object out of XML using following line. I get error shown below. Please see the detailed code below.
PersonType prs = (PersonType)xs.ReadObject(new MemoryStream(File.ReadAllBytes(sFileName)));
Error in line 3 position 58. Expecting element 'PersonType' from namespace 'http://service.a1.com/base1/2005/'.. Encountered 'Element' with name 'Person', namespace 'http://service.a1.com/base1/2005/'.
Command Used
svcutil.exe" "C:\Temp\S1\UseXSDExe\UseXSDExe\Sample2\Prs.xsd" /t:code /language:cs /out:C:\SPrxy.cs /dconly
Full code
(class generated by SvcUtils.exe)
[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("http://service.a1.com/base1/2005/", ClrNamespace="service.a1.com.base1._2005")]
namespace service.a1.com.base1._2005
{
using System.Runtime.Serialization;
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="PersonType", Namespace="http://service.a1.com/base1/2005/")]
public partial class PersonType : object, System.Runtime.Serialization.IExtensibleDataObject
{
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private string LastNameField;
private string FirstNameField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, EmitDefaultValue=false)]
public string LastName
{
get
{
return this.LastNameField;
}
set
{
this.LastNameField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=1)]
public string FirstName
{
get
{
return this.FirstNameField;
}
set
{
this.FirstNameField = value;
}
}
}
}
(code used for converting XML to object)
public static void convertToObject(string sFileName)
{
DataContractSerializer xs = new DataContractSerializer(typeof(PersonType));
PersonType Person = (PersonType)xs.ReadObject(new MemoryStream(File.ReadAllBytes(sFileName)));
}
(XSD)
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.a1.com/base1/2005/" xmlns:bse1="http://service.a1.com/base1/2005/" elementFormDefault="qualified">
<xs:complexType name="PersonType">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="LastName" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="FirstName" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="Person" type="bse1:PersonType"/>
</xs:schema>
(XML)
<?xml version="1.0" encoding="utf-8"?>
<pr:Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://service.a1.com/base1/2005/ Prs.xsd"
xmlns:pr="http://service.a1.com/base1/2005/">
<pr:LastName> Lane </pr:LastName>
<pr:FirstName> Fane </pr:FirstName>
</pr:Person>
I ran XSD.exe on same XSD file. Then I was able to convert XML to Object using XmlSerializer.Deserialize().
XSD does not have any attribute. I have validated XML against XSD.
Please let me know why Deserialize() fails.
Your XSD specifies a root element name and data type name that are different:
<xs:element name="Person" type="bse1:PersonType"/>
When svcutil.exe generates data contract classes for this type, it puts the type name into the data contract rather than the root element name. This appears to be intentional, see Svcutil generates wrong Name property value in DataContractAttribute. Perhaps it does this since the contract type itself could be re-used anywhere in the object graph, and there's no data contract equivalent of XmlRoot that applies only when the type in question is the document's root element.
As a workaround, you have a couple options:
Hardcode the expected root element name when constructing the serializer:
var xs = new DataContractSerializer(typeof(service.a1.com.base1._2005.PersonType), "Person", "http://service.a1.com/base1/2005/");
Preload the XML into an XDocument and use the actual root element name when constructing the serializer:
var doc = XDocument.Load(sFileName);
service.a1.com.base1._2005.PersonType person;
var xs = new DataContractSerializer(typeof(service.a1.com.base1._2005.PersonType), doc.Root.Name.LocalName, "http://service.a1.com/base1/2005/");
using (var reader = doc.CreateReader())
{
person = (service.a1.com.base1._2005.PersonType)xs.ReadObject(reader);
}
Or use XmlSerializer.
Consider this XSD:
<xsd:schema targetNamespace="http://foobar" xmlns:tns="http://foobar"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="IdAttribute">
<xsd:attribute name="id" type="xsd:token" />
</xsd:complexType>
<xsd:complexType name="FoobarType">
<xsd:sequence>
<xsd:element name="someIds" type="tns:IdAttribute" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Foobar" type="tns:FoobarType" />
</xsd:schema>
which results in the following, generated Java class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "FoobarType", propOrder = {"someIds"})
public class FoobarType {
#XmlElement(required = true)
protected List<IdAttribute> someIds;
// ...
}
Because IdAttribute only contains one String (id), I want to map these Strings directly into my FoobarType for easier usage. Therefore I wrote an XmlAdapter:
public class IdAttributeAdapter extends XmlAdapter<IdAttribute, String> { ... }
I've edited the generated class manually to verify my XmlAdapter works as expected (it does):
#XmlElement(required = true)
#XmlJavaTypeAdapter(IdAttributeAdapter.class)
protected List<String> someIds;
Of course I want to have this little code change done during code generation, so I've added the following bindings file (src/main/xjb/bindings.xjb):
Attempt 1:
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jaxb:bindings schemaLocation="../resources/xsd/foobar.xsd">
<jaxb:bindings node="//xsd:complexType[#name='IdAttribute']">
<xjc:javaType
name="java.lang.String"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/foobar/src/main/xjb/bindings.xjb;
lineNumber: 12; columnNumber: 91;
compiler was unable to honor this conversion customization. It is attached to a wrong place, or its inconsistent with other bindings.
Attempt 2 (from here):
<jaxb:globalBindings xmlns:foo="http://foobar" xsi:schemaLocation="http://foobar ../resources/xsd/foobar.xsd">
<xjc:javaType
name="java.lang.String"
xmlType="foo:IdAttribute"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:globalBindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/projects/luna/oasch/src/main/xjb/bindings.xjb;
lineNumber: 8; columnNumber: 112;
undefined simple type "{http://foobar}IdAttribute".
I've tried a few variations, but they all resulted in similar errors. So what's the correct way to add an XmlApender using a bindings file?
What you try to do is wrong.
You should replace fully the xml type IdAttribute with the java type String.
You will have to work with generic dom.Element type and convert it <-> String.
Here is an exemple : JAXB #XmlAdapter for arbitrary XML for your case replace the java type Bar with String.
Other solution would be to add behavior to the generated class so that you add methods to your FoobarType to work as if it has a List of String
e.g. add methods
List<String> getIds()
void setIds(List<String>)
Here is a link that explain how you can do this : "Adding behavior"
I have these two classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class OpportunityLocation
{
#XmlElement
private LatitudeLongitude coordinates;
[...]
}
public class LatitudeLongitude implements Serializable
{
private BigDecimal latitude;
private BigDecimal longitude;
[...]
}
when OpportunityLocation is serialized I get
<location>
[...]
<coordinates>
<latitude>51.53684899999999657893567928113043308258056640625</latitude>
<longitude>-0.1325880000000000114024345521102077327668666839599609375</longitude>
</coordinates>
Now I've been asked to provide an xsd to validate the xml, I set the type of latitude and longitude to xsd:decimal but the validator complains that
Element 'latitude': '51.53684899999999657893567928113043308258056640625' is not a valid value of the atomic type 'xs:decimal'.
(same for longitude)
The xsd fragment describing the coordinates is
<xs:complexType name="latitudeLongitude">
<xs:all>
<xs:element name="latitude" type="xs:decimal"/>
<xs:element name="longitude" type="xs:decimal"/>
</xs:all>
</xs:complexType>
I think the best solution would be to truncate the lat/long values, as there is no point to have that much precision in the first place.
How do I instruct JAXB to do that?
Or simply use BigDecimal.setScale(). No need for an XmlAdapter if applicable to your case.
BigDecimal bd = new BigDecimal((double) 1.2345);
bd = bd.setScale(2, RoundingMode.HALF_UP);
You can do this using an XmlAdapter. Below is a similar example demonstrating the concept with the Joda-Time classes:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
When I try to generate a client from a wsdl document, I get a client that seems to have a lot of JAXBElement atributes, for instance
protected List<JAXBElement<?>> nameOrLinkingNameOrFamilyName;
I use soapUI to generate with apache cxf 2.3.3 as tool, also as config file the following:
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
As far as I saw this is related with the choice tags in the wsdl document.
thanks in advance
A JAXBElement will be generated for choice properties where multiple XML elements will correspond to the same Java class. This is in order to preserve information about the element since this can not be derived from the type of the value.
binding.xml
The following JAXB schema bindings file will ensure that a choice property is generated:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<globalBindings choiceContentProperty="true"/>
</bindings>
XML Schema That Will Produce Object Property
In this version of the XML schema, all of the XML elements will correspond to a different Java class:
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is sufficient to uniquely identify the element, the property does not contain a JAXBElement to preserve this information:
#XmlElements({
#XmlElement(name = "address", type = Address.class),
#XmlElement(name = "phone-number", type = PhoneNumber.class),
#XmlElement(name = "note", type = String.class)
})
protected Object addressOrPhoneNumberOrNote;
XML Schema That Will Produce JAXBElement Property
Now we will modify the choice structure so that both the note and email methods will correspond to the String class.
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
<xsd:element name="email" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is no longer sufficient to uniquely identify the element, the property must contain a JAXBElement to preserve this information:
#XmlElementRefs({
#XmlElementRef(name = "phone-number", type = JAXBElement.class),
#XmlElementRef(name = "email", type = JAXBElement.class),
#XmlElementRef(name = "address", type = JAXBElement.class),
#XmlElementRef(name = "note", type = JAXBElement.class)
})
protected JAXBElement<?> addressOrPhoneNumberOrNote;
For More Information
http://blog.bdoughan.com/2011/04/xml-schema-to-java-xsd-choice.html
A very generalized and simplified scenario description:
Application builds JAXB instances through UI,
The JAXB instances are built in different steps are composed in the end,
As this is done, I must perform validation of the JAXB instances created in each step against the XSD to verify that the UI tier does not build nonsense.
Now the schema defines complex types A, B and C. The schema specifies that XML document must have top level element A and this can contain multiple B and this can optionally include C. I create JAXB instance for B and absolutely want to validate this against the complex type definition in the XSD before nesting it under A. However validation of this will fail if I validate against the entire XSD.
Questions: How to validate JAXB instance against only a part of the XSD from which its class has been generated from?
How about using schemagen to generate schema from the JAXB class which instance I want to validate and then validate against that? Do you think that can work? Any other ideas?
I have no previous experience with schemagen and will start prototyping of this solution soon.
Note: in reality, the schemas are not as simple as in the above example and the solution of creating some always-valid mock of A is not feasible option. Not to mention that this kind of validation will be on hundred places to say the least.
create separate schema for element you want to validate, where this element is on root level.
to workaround missing #xmlRoottag see 101 ways to marshal objects with JAXB
Well it turns out that using xsi:type let's one accomplish this.
http://www.w3.org/TR/xmlschema-1/#xsi_type
While working with xjc and schemagen tools please consider same concept which we use in java. In java, every class is a skeleton and object is an instance. Same as we need to consider XSD as skeleton and XML as instance.
xjc tool:- Xsd to Java Class Or Unmarshaling
Consider the below XSD with namespaces for example. The xjc tool will generate the java class along with package-info and object-factory.
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ir="http://irctc.org/service" targetNamespace="http://irctc.org/service">
<xsd:element name="Customer" type="ir:CustomerType"/>
<xsd:complexType name="CustomerType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="src" type="xsd:string"/>
<xsd:element name="dest" type="xsd:string"/>
<xsd:element name="price" type="xsd:float" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Use below command
D:\>xjc Customer.xsd
parsing a schema...
compiling a schema...
org\irctc\service\CustomerType.java
org\irctc\service\ObjectFactory.java
org\irctc\service\package-info.java
Note :- If you have multiple java class then you can use jaxb.index file instead of using ObjectFactory.java.
schemagen tool:- Java Class to Xsd Or Marshaling
Let us suppose we want to generate the Xsd file using java class then first we need to create the ObjectFactory.java Or jaxb.index file and package-info.java in the same package where we have the CustomerType.java.
package org.irctc.service;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement("Customer")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CustomerType", propOrder = {
"name",
"src",
"dest",
"price"
})
public class CustomerType {
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected String src;
#XmlElement(required = true)
protected String dest;
protected Float price;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getSrc() {
return src;
}
public void setSrc(String value) {
this.src = value;
}
public String getDest() {
return dest;
}
public void setDest(String value) {
this.dest = value;
}
public Float getPrice() {
return price;
}
public void setPrice(Float value) {
this.price = value;
}
}
Use below command
D:\>schemagen org.irctc.service.CustomerType
Note: Writing D:\schema1.xsd
The above command will generate the xsd file. The package of java class will considered as xsd namespaces.
The above details are only for understanding marshaling and unmarshaling process using tools in Jax-B API.
For more details , check below examples
Jax-B Hello World Example
Java-XML mapping with Jax-B 2.0