I'm using JAX-B to generate POJOs from a predefined XSD.
I encountered this element definition
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://order.example.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://order.example.com"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="Order">
<xs:complexType>
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:choice minOccurs="0" maxOccurs="3">
<xs:element name="Credit" type="CreditType" form="qualified"/>
<xs:element name="GiftCard" type="GiftCardType" form="qualified"/>
</xs:choice>
<xs:any namespace="##other" processContents="lax"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="CreditType">
<xs:sequence>
<xs:element name="Number" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="GiftCardType">
<xs:sequence>
<xs:element name="Code" type="xs:long" />
</xs:sequence>
</xs:complexType>
</xs:schema>
As you notice there's an awful combination of a nested <choice/> with <any/>.
XJC generates this code:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"creditOrGiftCardOrAny"
})
#XmlRootElement(name = "Order")
public class Order {
#XmlElementRefs({
#XmlElementRef(name = "GiftCard", namespace = "http://order.example.com", type = JAXBElement.class, required = false),
#XmlElementRef(name = "Credit", namespace = "http://order.example.com", type = JAXBElement.class, required = false)
})
#XmlAnyElement(lax = true)
protected List<Object> creditOrGiftCardOrAny;
}
Where the property references JAXBElements instead of the generated classes of CreditType and GiftCardType.
If I try to marshal an Order object like this:
Order order = new Order();
CreditType credit = new CreditType();
credit.setNumber("4111111111111111");
order.getCreditOrGiftCardOrAny().add(credit);
JAXBContext jaxbContext = JAXBContext.newInstance(Order.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(order, System.out);
I get the following error:
[com.sun.istack.internal.SAXException2: unable to marshal type "com.example.order.CreditType" as an element because it is missing an #XmlRootElement annotation]
Which can be solved by adding a binding that forces XJC to annotate the class, the problem is that in my case I have many occurrences of this issue and so having to add a binding for each type doesn't scale very well.
Anyone has a better suggestion?
I uploaded a maven project reproducing the issue: https://github.com/jcfandino/jaxb-choice
Just run:
git clone https://github.com/jcfandino/jaxb-choice.git && cd jaxb-choice
mvn test
Related
for the following mapping
<xs:complexType name="ParentNode">
<xs:sequence>
<xs:element name="child" type="ChildNode"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ParentNodeRestriction">
<xs:complexContent>
<xs:restriction base="ParentNode">
<xs:sequence>
<xs:element name="child" type="ChildNodeRestriction"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
Jaxb generates the following classes (approximately)
public class ParentNode {
protected ChildNode child;
}
public class ParentNodeRestriction
extends ParentNode
{
}
public class ChildNode {
}
public class ChildNodeRestriction
extends ChildNode
{
}
Knowing that ParentNode is not used elsewhere than ParentNodeRestriction, is it possible to force JAXB to generate the following class (hopefully without modifying the mapping):
public class ParentNodeRestriction
{
protected ChildNodeRestriction child;
}
i.e. I need to have the most precise type for the child field.
ps : I am using jaxb2-maven-plugin version 2.2 with maven
Thank you
Using the xjb directive <xjc:treatRestrictionLikeNewType/> (as indicated by his name) generates classes for restrictions which do not inherit from the class corresponding to the parent mapping.
This solves the problem.
There seems to be a slight annoying consequence : elements with maxOccurs="0" are generated despite the <xjc:simple/> directive.
In fact, elements with maxOccurs="0" can be removed from the restriction definition.
I have a class which contains inner enum type.
#XmlRootElement
public class Address {
#XmlEnum
#XmlType(name="addressType")
public static enum Type {
}
}
Here is my package-info.java.
#XmlSchema(
attributeFormDefault = XmlNsForm.UNQUALIFIED,
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://some",
xmlns = {
#XmlNs(prefix = "xsi",
namespaceURI = XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)
}
) #XmlAccessorType(XmlAccessType.NONE)
package some;
It works find when I use JAXBContex#generateSchema.
But org.codehaus.mojo:jaxb2-maven-plugin:schemagen generates separate schemas.
One for address which has a namespace,
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="..."
targetNamespace="...">
<xs:complexType name="address">
...
<xs:sequence>
...
<xs:element name="type" type="addressType"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
and the other for addressType which has no namespace.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
<xs:simpleType name="addressType">
<xs:restriction base="xs:string">
<xs:enumeration value="CC"/>
<xs:enumeration value="BCC"/>
<xs:enumeration value="TO"/>
<xs:enumeration value="REPLY_TO"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Which one is right?
The XML schema generator schemagen which comes along with JAXB (and JDK) generates two .xsd files from class Address (as shown), very much like the ones you have posted as output from the maven plugin. However, the first .xsd file contains
<xs:import schemaLocation="schema2.xsd"/>
which is not in your first .xsd. Did you remove this element? With this element, there is no reason to doubt the pair of .xsd files.
It would have been nice to see the single .xsd file.
You can also use xjc to generate Java classes from the .xsd files and compare the results.
I have java class:
public class ActivityAddress {
#XmlElement(name = "Elem1", required = false)
private String elem1;
#XmlElement(name = "Elem2", required = false)
private String elem2;
#XmlElement(name = "PostIndex", required = true)
private String postIndex;
}
I want to get schema like this:
<xs:complexType name="ActivityAddress">
<xs:sequence>
<xs:choice minOccurs="0">
<xs:element name="Elem1" type="xs:string"/>
<xs:element name="Elem2" type="xs:string"/>
</xs:choice>
<xs:element name="PostIndex" type="xs:string"/>
</xs:sequence>
So the two fields "Elem1" and "Elem2" must be in choice.
Decision like this:
#XmlElements({
#XmlElement(name = "Elem1", type = String.class, required = false),
#XmlElement(name = "Elem2", type = String.class, required = false)
})
private String elem;
isn't suitable for me, because in java class i need have both fields.
Ноw i can do it? Can anyone please help?
Generating an XML Schema
For the following Java class:
public class ActivityAddress {
#XmlElement(name = "Elem1", required = false)
private String elem1;
#XmlElement(name = "Elem2", required = false)
private String elem2;
#XmlElement(name = "PostIndex", required = true)
private String postIndex;
}
You are going to get an XML Schema like the following, there isn't a way to generate the choice the way you would like.
<xs:sequence>
<xs:element name="Elem1" type="xs:string" mincOccurs="0"/>
<xs:element name="Elem2" type="xs:string" minOccurs="0"/>
<xs:element name="PostIndex" type="xs:string"/>
</xs:sequence>
Pointing to a Hand Crafted XML Schema
You can however generate an XML Schema and then modify it yourself and then tell JAXB to use it. This is done with the package level #XmlSchema annotation.
package-info.java
#XmlSchema(location="http://www.example.com/foo/mySchema.xsd")
package com.example.foo;
I have an xsd, in which each element name and its complex type name are same. This creates problem when I am generating the Java from xsd using JAXB. This xsd has been provided by external service provider. Is the XSD wrong or I am doing something wrong in generating the xsd. Following is the sample of the xsd:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="unqualified">
<xs:element name="Discounts" type="Discounts"/>
<xs:complexType name="Discounts">
<xs:complexContent>
<xs:extension base="listWithSizeAttribute">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Discounttest" type="Discounttest"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Whenever I generate the Java object using JAXB, i get the error which says duplicate element Discounts. The entire xsd is full of such elements. Hence, I think something is wrong at my side.
Thanks,
Akshay
XML Schema
There isn't a problem when a global element and a complex type have the same name. I have flushed out your XML schema to make it valid so that I can run a schema compile on it.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified" elementFormDefault="unqualified">
<xs:element name="Discounts" type="Discounts" />
<xs:complexType name="listWithSizeAttribute" />
<xs:complexType name="Discounttest" />
<xs:complexType name="Discounts">
<xs:complexContent>
<xs:extension base="listWithSizeAttribute">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0"
name="Discounttest" type="Discounttest" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Discounts
A JAXB (JSR-222) implementation generates Java classes for complex types, so you a class will be generated for the Discounts complex type.
package forum11981855;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Discounts", propOrder = {
"discounttest"
})
public class Discounts
extends ListWithSizeAttribute
{
#XmlElement(name = "Discounttest")
protected List<Discounttest> discounttest;
public List<Discounttest> getDiscounttest() {
if (discounttest == null) {
discounttest = new ArrayList<Discounttest>();
}
return this.discounttest;
}
}
ObjectFactory
An #XmlElementDecl annotation will be generated on the ObjectFactory class corresponding to the global element Discounts.
package forum11981855;
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 _Discounts_QNAME = new QName("", "Discounts");
public ObjectFactory() {
}
public Discounttest createDiscounttest() {
return new Discounttest();
}
public Discounts createDiscounts() {
return new Discounts();
}
public ListWithSizeAttribute createListWithSizeAttribute() {
return new ListWithSizeAttribute();
}
#XmlElementDecl(namespace = "", name = "Discounts")
public JAXBElement<Discounts> createDiscounts(Discounts value) {
return new JAXBElement<Discounts>(_Discounts_QNAME, Discounts.class, null, value);
}
}
For More Information
http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html
I have the following elements in my schema:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="optimizeModelBase">
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
<xs:complexType name="riskModel">
<xs:complexContent>
<xs:extension base="optimizeModelBase">
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="fullCovariance">
<xs:complexContent>
<xs:extension base="optimizeModelBase">
<xs:attribute name="fromDate" type="xs:date" use="required"/>
<xs:attribute name="toDate" type="xs:date" use="required"/>
<xs:attribute name="windowSize" type="xs:int" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
In my main schema body, I use a element to specify a 1-of situation:
<xs:choice id="RiskModelParameter">
<xs:element name="RiskModel" type="riskModel"/>
<xs:element name="FullCovariance" type="fullCovariance"/>
</xs:choice>
When I run xsd.exe, the resulting code is:
[System.Xml.Serialization.XmlElementAttribute("FullCovariance",
typeof(fullCovariance))]
[System.Xml.Serialization.XmlElementAttribute("RiskModel",
typeof(riskModel))]
public optimizeModelBase Item
{
get
{
return this.itemField;
}
set
{
this.itemField = value;
}
}
The issue is that the element's ID tag is being ignored, and xsd.exe is arbitrarily naming the property "Item". I have to admit, it's not a big issue, but it's starting to annoy me. What makes this extra annoying is that if I have additional elements at the same level, xsd.exe binds them as "Item1", "Item2", etc.
Does anyone know if it's possible to not have xsd.exe name my choice elements as "Item", and instead be able to put in my own property names?
I've searched high and low, but it seems like there is no solution for my problem. According to the link:
http://msdn.microsoft.com/en-us/library/sa6z5baz(VS.80).aspx
It seems like the arbitrary naming of the choice element is not overrideable. Hopefully this information is helpful to others out there...!
Just had this exact problem today.
I do have a workaround so you can circumvent this problem using a group instead of choice.
using the above xsd as the basis:
Rewrite:
<xs:choice id="RiskModelParameter">
<xs:element name="RiskModel" type="riskModel"/>
<xs:element name="FullCovariance" type="fullCovariance"/>
</xs:choice>
To:
<xs:group name="RiskModelGroup">
<xs:sequence>
<xs:element name="RiskModel" type="riskModel"/>
<xs:element name="FullCovariance" type="fullCovariance"/>
</xs:sequence>
</xs:group>
Reference the group in your element:
<xs:element name="Foo">
<xs:complexType>
<xs:sequence>
<xs:element name="SomeFieldId" type="xs:int" />
<xs:group ref="RiskModelGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
Run xsd.exe and the result is as follows
public partial class Foo {
private int someFieldIdField;
private riskModel riskModelField;
private fullCovariance fullCovarianceField;
/// <remarks/>
public int SomeFieldId {
get {
return this.someFieldIdField;
}
set {
this.someFieldIdField = value;
}
}
/// <remarks/>
public riskModel RiskModel {
get {
return this.riskModelField;
}
set {
this.riskModelField = value;
}
}
/// <remarks/>
public fullCovariance FullCovariance {
get {
return this.fullCovarianceField;
}
set {
this.fullCovarianceField = value;
}
}
}
Now you have property names RiskModel and FullCovariance
Foo f = new Foo()
f.RiskModel.name
or
f.FullCovariance.fromDate
If you require multiple Items of RiskModels and FullCovariance objects you could add a new element having the RiskModelGroup within a sequence.
Good luck!
For those who still want to know : Just use XSDObjectGenerator (made by Microsoft). It manage XsdChoice by adding one object for each choice to your class. That way, you don't have to look for the right element to use for the Item property.
For example:
<xs:complexType name="AccountSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalAccountIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
became
[XmlType(TypeName = "AccountSchemeName1Choice", Namespace = Declarations.SchemaVersion), Serializable]
public class AccountSchemeName1Choice : INotifyPropertyChanged
{
[XmlElement(ElementName = "Cd", IsNullable = false, Form = XmlSchemaForm.Qualified, DataType = "string", Namespace = Declarations.SchemaVersion)]
public string __Cd;
[XmlIgnore]
public string Cd
{
get { return __Cd; }
set { __Cd = value; RaisePropertyChanged("Cd"); }
}
[XmlElement(ElementName = "Prtry", IsNullable = false, Form = XmlSchemaForm.Qualified, DataType = "string", Namespace = Declarations.SchemaVersion)]
public string __Prtry;
[XmlIgnore]
public string Prtry
{
get { return __Prtry; }
set { __Prtry = value; RaisePropertyChanged("Prtry"); }
}
public AccountSchemeName1Choice()
{
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I once actually parsed an autogenerated xsd.exe file and changed it using NRefactory.
I myself just encountered this limitation in my dealings with XSD.exe. It seems that this is a fairly uniform practice with respect to how XSD.exe interprets the ID attribute for all element types. I'd love to hear a rationalization from someone on the MS development team for why XSD.exe works in this manner.
What's interesting is that I have been working on a SchemaImporterExtension that would, among other things, leverage the ID attribute of choice elements to achieve precisely what you're describing, a means of customizing the field/property names of choice mapped object members.
Unfortunately, not only does it seem that XSD.exe doesn't support ID attribute binding, but it doesn't even appear that ID is included in the XmlSchemaChoice object that reflects the choice element from the parsed schema document.
Perhaps I'm missing something, but if this is indeed the intended behavior and not an error on my part, then it's a pretty ridiculous omission in my estimation, and it speaks to just how much of a neutered XSD representation is reflected in the MS schema auto-generation tools.
XSD.exe adheres to what I'll term as a "lean and mean" interpretation of the XML schema standard. Apparently now WCF obviates XSD.exe, and guess what? WCF's svcutil tool recognizes an even smaller footprint of XML schema; choice element binding is even't supported in svcutil IIRC.
At some point I'm not even sure what value the MS auto-generation tools are going to bring to the table, because you're going to be reduced to using trivial XSD structures (I mean, no choice element support? Really?) to encapsulate your business object modeling.