I'm working on a project in java where xml input is read via xjc into generated classes. So the xsd schema is converted into java type.
All is well, and usually I have no problems, but I can't seem to get this conversion right: I want to convert an xml list of enums into a java list of enums.
This is my xsd part:
<xs:attribute name="myTypes" use="required">
<xs:simpleType >
<xs:list itemType="output.myType"/>
</xs:simpleType>
</xs:attribute>
<xs:simpleType name="output.myType">
<xs:annotation>
<xs:appinfo>
<jaxb:javaType
name="packagename.MyEnumType"
parseMethod="...TypeConverter.unmarshalOutputMyType"
printMethod="...TypeConverter.marshalOutputMyType"
/>
</xs:appinfo>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="option1"/>
<xs:enumeration value="option2"/>
<xs:enumeration value="option3"/>
</xs:restriction>
</xs:simpleType>
When doing the xjc generation I keep getting this in the generated java class:
public List<String> getMyTypes()
While I'm expecting this:
public List<MyEnumType> getMyTypes()
How can I make jaxb/xjc to use the List for the generated list, in stead of just parsing the type into a List of Strings? The conversion works perfect if I'm just using the output.myType directly as simpleType for the attribute, but it doesn't work when I try to make the attribute into a list of those items.
Related
I have received a specification for a SOAP service where the request that I'm sent will contain the following:
<eventContexts>
<eventContext name="eventType" value="Unwind"/>
<eventContext name="referenceId" value="26214"/>
</eventContexts>
I am trying to model this object in the XSD but I'm blocked in the choice of type for the attribute value. As you can see in the above example, it can either be a xs:string (case Unwind) or a xs:long (case 26214).
What type should I choose to make the attribute value accept both xs:string and xs:long?
So far I can think of two things:
1) Should I create two different attributes, e.g. stringValue and longValue:
<xs:complexType name="XmlEventContext">
<xs:attribute name="name" type="xs:string"/>Sh
<xs:attribute name="stringValue" type="xs:string" minOccurs="0"/>
<xs:attribute name="longValue" type="xs:long" minOccurs="0"/>
</xs:complexType>
... and let the client send me the good value in the good attribute? (This looks ugly to me but I'm not a big expert).
2) Should I extend the auto-generated class XmlEventContext with a custom class that takes the value as xs:string and then tries to cast it to xs:long?
public class XmlEventContextComplete extends XmlEventContext {
//code to manage a property referenceId which can either be long or string
}
3) Any other more elegant suggestion?
Thanks in advance!
You want a type that can be either a long or a specific enumerated string.
This should cover that case:
<xs:complexType name="xmlEventContext">
<xs:attribute name="name" type="xs:string" />
<xs:attribute name="value" type="longOrUnwind" />
</xs:complexType>
<xs:simpleType name="longOrUnwind">
<xs:union memberTypes="xs:long unwindConstant" />
</xs:simpleType>
<xs:simpleType name="unwindConstant">
<xs:restriction base="xs:string">
<xs:enumeration value="Unwind" />
</xs:restriction>
</xs:simpleType>
I'm having a problem generating enum from deeply nested xsd elements. When I generate the code during maven build, my enums are of type string. Here's an example.
<xs:element name="Car">
<xs:complexType>
<xs:sequence>
<xs:element name="CarModal">
<xs:complexType>
<xs:sequence>
<xs:element name="Type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="SomeValue"/>
<xs:enumeration value="AnotherValue"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:completType>
</xs:element>
</xs:sequence>
From the above example we would have another 20 elements inside of Car element that contains enum value name 'Type'. I have a binding file to bind 'Type' to jaxb:typesafeEnumClass but it's not working, i'm still getting strings as my enum type. Here's an example of my binding.
<jaxb:bindings schemaLocation="someLocation">
<jaxb:bindings node="//xs:element[#name='Car']>
<jaxb:bindings node="//xs:element[#name='CarModal']">
<jaxb:bindings node="xs:element[#name='Type']/xs:simpleType>
<jaxb:typesafeEnumClass name="Type"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Thanks for any help and I can't change the xsd
I guess your bindings are nor precise enough. When you write //xs:element[#name='CarModal'], you basically say "any CarModal element inside my schema". Next, you say you have many Type elements so xs:element[#name='Type']/xs:simpleType is not precise enough.
Try more precise expressions like
xs:complexType/xs:sequence/xs:element[#name='CarModal']/
xs:complexType/xs:sequence/xs:element[#name='Type']/xs:simpleType
Next, your binding makes a general impression of the incorrect syntax. For instance this:
<jaxb:bindings node="xs:element[#name='Type']/xs:simpleType>
is invalid XML (missing " after xs:simpleType). So it might be the case that you binding is not considered at all - otherwise you should have gotten an error instead of generated code. Double-check if the binding is applied at all.
I have the following structure in a JAR:
Products.xsd
ProductCommonTypes.xsd
/com
/foo
Products.class
When I do
schemaURL = Products.class.getClassLoader().getResource(schemaFile);
I see "Using schemaURL: jar:file:/C:/my.jar!/Products.xsd". I think this is good.
Later, I try to create a Schema and get an exception stating "Cannot resolve the name 'common:nonEmptyString' to a(n) 'type definition' component."
I believe that the problem is that it is unable to find common:nonEmptyString (which is in ProductCommonTypes.xsd) but can not figure out how to fix it.
Products.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:p="http://www.foo.com/Products"
targetNamespace="http://www.foo.com/Products"
xmlns:common="http://www.foo.com/ProductsCommonTypes"
elementFormDefault="qualified">
<xs:import schemaLocation="ProductsCommonTypes.xsd"
namespace="http://www.foo.com/ProductsCommonTypes"/>
<xs:element name="products">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="common:nonEmptyString" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
ProductCommonTypes.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.foo.com/ProductsCommonTypes"
xmlns="http://www.foo.com/ProductsCommonTypes"
elementFormDefault="qualified" >
<xs:simpleType name="nonEmptyString">
<xs:annotation>
<xs:documentation>A type that will require a non-empty string value be present
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="(\s*[^\s]\s*)+"></xs:pattern>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="url">
<xs:annotation>
<xs:documentation>A HTTP URL</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:anyURI">
<xs:pattern value="https?://.+"></xs:pattern>
</xs:restriction>
</xs:simpleType>
</xs:schema>
[jaxb - Unable to unmarshall from XSD which included other xsd
suggests adding all schema references, but I am working with many schemas that all import several other schemas. Seems like it could get ugly quick!
Is there a generic/dynamic solution that does not require me to identify and hardcode all schemas everytime I plan on working with one that might import others?
I plan to move all XSDs to a MyXSDs folder within the jar at some point. After moving them, will I have to do anything differently due to the location change?
Solution
I was able to solve this using an XMLCatalogResolver and creating an XML catalog file.
I moved all of my XSDs into a single folder and have the following structure in my JAR:
MyXSDs
Products.xsd
ProductCommonTypes.xsd
/com
/foo
Products.class
First, I created the catalog file and identified the schema that was being referenced.
<!DOCTYPE catalog
PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<public
publicId="http://www.foo.com/ProductCommonTypes"
uri="ProductCommonTypes.xsd"/>
</catalog>
Then, I created an XMLCatalogResolver that uses my catalog file and used it as the ResourceResolver for my SchemaFactory. It is important to note that the catalogs must be specified as an ordered array of absolute URIs as per the constructor's documentation.
// The catalogs must be an ordered array list of *ABSOLUTE* URIs
String[] catalogs = new String[] { xmlCatalogAbsPath };
XMLCatalogResolver resourceResolver = new XMLCatalogResolver(catalogs);
schemaFactory.setResourceResolver(resourceResolver);
You must specify the relative path to the XSD file. Therefore, because I put all of mine into a folder, I set relatativeSchemaPath="MyXSDs/Products.xsd" for use with ClassLoader.getSystemResource.
StreamSource streamSource = new StreamSource(ClassLoader.getSystemResource(relatativeSchemaPath).toString());
Schema schema = schemaFactory.newSchema(streamSource);
I'm trying to generate java classes using XJC and I have the following problem:
I'm trying to parse this schema which is a big enum type (bigger than default typesafeEnumMaxMembers). So I use following binding:
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<!-- Raise theEnumMemberSizeCap limit -->
<jxb:bindings >
<jxb:globalBindings typesafeEnumMaxMembers="2000"/>
</jxb:bindings>
</jxb:bindings>
and then I call xjc with following line:
C:\Program Files\Java\jdk1.7.0_17\bin\xjc.exe -d C:\Users\buriak\out xml\dAllDocuments_v02.1.xsd xml/binding.xjb
this gives no errors, just that:
parsing a schema...
compiling a schema...
Then it ends without creating anything.
Smaller enum types are easily parsed same way, but bigger once are just ignored and if they are a part of some other type other xsd - they simply become a string:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="dAllDocuments_v02.1.xsd"/>
<xs:complexType name="tDocument">
<xs:annotation>
<xs:documentation>Документ - описание</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Code_Document" type="dAllDocuments">
<xs:annotation>
<xs:documentation>Код документа</xs:documentation>
</xs:annotation>
</xs:element>
...............
public class TDocument {
#XmlElement(name = "Code_Document", required = true)
protected String codeDocument;
...............
Using JAXB or XmlBeans results with nothing aswell.
Hope somebody knows what to do with that.
EDIT
After browsing without finding answers for long time I started to think, that it is not the size that is the problem.
I was right and as a result I found this - JAXB enumeration with numeric values
But XSD which I'm trying to parse is very big:
<xs:simpleType name="dAllDocuments">
<xs:restriction base="xs:string">
<xs:enumeration value="008001000000"/>
<xs:enumeration value="008001001000"/>
<xs:enumeration value="008001002000"/>
<xs:enumeration value="008001003000"/>
<xs:enumeration value="008001004000"/>
<xs:enumeration value="008001005000"/>
<xs:enumeration value="008001006000"/>
<xs:enumeration value="008001007000"/>
<xs:enumeration value="008001008000"/>
<xs:enumeration value="008001009000"/>
<xs:enumeration value="008001010000"/>....
And it keeps going for long more. There is absolutely no way I can write them all down in such a way:
<jxb:bindings node="//xs:simpleType[#name='dAllDocuments']/xs:restriction/xs:enumeration[#value='008001000000']">
<jxb:typesafeEnumMember name="OOBOOIOOOOOO"/>
</jxb:bindings>
Is there any way to make this work other than specifying name for each value manually?
I mean I can create a program which will make that kind of stuff using Strings, but is there any smart way?
Because actual XSD that I'm parsing is connected to multiple of such ENUM XSD and I need to parse them all.
After spending some more time on research I finally managed to get results which I was waiting for from xjc.
Here is binding file I was using, so that I don't get warning for enum type being too big and so that all those big enum types would have a generated name like VALUE_1, VALUE_2 etc.
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<!-- Raise theEnumMemberSizeCap limit -->
<jxb:bindings >
<jxb:globalBindings typesafeEnumMaxMembers="2000" typesafeEnumMemberName="generateName"/>
</jxb:bindings>
<jxb:bindings schemaLocation="STD_Region_Cadastr.xsd">
<jxb:bindings node="//xs:complexType[#name='tRight_Owner']">
<jxb:class name="tRight_Owner2"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Also I'd like to mention that, since XSD files, which I was parsing, had a lot of cyrillic letters, here is xjc call I was using:
C:\Program Files\Java\jdk1.7.0_17\bin>xjc.exe -b xml/binding.xjb -d C:\Users\buriak\out xml\STD_Region_Cadastr.xsd -encoding UTF-8
That makes almost every cyrillic letter enterance show in java files correctly.
That's it.
Is it possible to get restrictions of specific element when I'm parsing XML file in Java?
For example, if I have a schema:
<xs:element name="MyString" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
when I get node MyString during XML file parsing I want to be able to acquire information that its restrictions are xs:string and maxLength = 100.
I would recommend this project.
It is the same jaxb plugin as Puce linked, but with proper maven project and some improvements.
You could look for/ write a JAXB plugin to generate Bean Validation Annotations (JSR-303), e.g.:
http://metro.1045641.n5.nabble.com/JAXB-plugin-to-generate-Bean-Validation-Annotations-JSR-303-td5598189.html