Parsing XSD and getting nested elements - java

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.

Related

JAXB Map with abstract class values

I have the following Map that contains either Double or Integer instances as value, both of which extend the abstract class Number, annotated as follows:
#XmlElement(name="settings")
private Map<String,Number> settings;
It's marshalled into XML as:
<settings>
<entry>
<key>SETTING_A</key>
<value xsi:type="xs:double">0.03</value>
</entry>
<entry>
<key>SETTING_B</key>
<value xsi:type="xs:int">1000</value>
</entry>
</settings>
I also need to provide the XSD for this type of XML, and here is where I have problems. I tried to define a complexType with a choice, but doesn't work because the element that contain the int, and the element that contains the double can't have the same name (ie. they can't be both named "value"), which is exactly what the JAXB generated XML does.
The following XSD would work, but I don't want to have different element names ( or ):
<xsd:complexType name="Entry">
<xsd:sequence>
<xsd:element name="key" type="NimrodSettingKey" minOccurs="1" maxOccurs="1"/>
<xsd:choice>
<xsd:element name="double" type="xsd:double" minOccurs="1" maxOccurs="1"/>
<xsd:element name="int" type="xsd:integer" minOccurs="1" maxOccurs="1"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
Any suggestions on how should I specify that the element could be an int or a double in my XSD document?
Thanks!
EDIT #1
#svasa suggested using the following approach in my XSD, which give the same name to an element inside a choice:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Settings"/>
<xsd:complexType name="Settings">
<xsd:sequence>
<xsd:element name="key" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:choice>
<xsd:element name="value" xs:type="xsi:double" minOccurs="1" maxOccurs="1" />
<xsd:element name="value" xs:type="xsi:integer" minOccurs="1" maxOccurs="1" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
but my XSD editor complained with this error:
complex type 'Settings' violates the unique particle attribution rule in its components 'value' and 'value'
which I think means that the two elements under the must have different names each, they can't both be named "value".
I finally opted for refactoring my code from:
#XmlElement(name="settings")
private Map<String,Number> settings
to:
#XmlElementWrapper(name="settings")
#XmlElement(name="setting")
private List<Setting> settings;
My Settings class was renamed to Setting and now stores one single setting with attributes:
#XmlAttribute(name="name")
private String name;
#XmlAttribute(name="intValue")
private Integer intValue;
#XmlAttribute(name="doubleValue")
private Double doubleValue;
And when marshalled into XML, it produces the elements that either have doubleValue or intValue attributes (but the element is named in both cases):
<settings>
<setting name="SETTING_A" doubleValue="1.0E-5"/>
<setting name="SETTING_B" intValue="1000"/>
...
</settings>
And the XML generated is valid when the XSD contains:
<xsd:complexType name="Settings">
<xsd:sequence>
<xsd:element name="setting" type="Setting" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Setting">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="doubleValue" type="xsd:double"/>
<xsd:attribute name="intValue" type="xsd:int"/>
</xsd:complexType>
The xs:choice should enable you to specify integer or double, and maxOccurs="1" will take care of only one value.
But if you want the key to be unique, you could use xsd:unique like below:
<xsd:element name="settings">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" name="entry">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="key" type="NimrodSettingKey" minOccurs="1" maxOccurs="1" />
<xsd:choice>
<xsd:element name="value" xsi:type="xsd:double" minOccurs="1" maxOccurs="1" />
<xsd:element name="value" xsi:type="xsd:integer" minOccurs="1" maxOccurs="1" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:unique name="keyU">
<xsd:selector xpath=".//key" />
<xsd:field xpath="." />
</xsd:unique>
</xsd:element>
This section :
<xsd:unique name="keyU">
<xsd:selector xpath=".//key" />
<xsd:field xpath="." />
</xsd:unique>
adds an unique constraint that specifies your keys should be unique.

SOA model Schema parsing error for complex types

The schema parser is able to parse the ComplexTypes of form:
<xsd:complexType name="PersonType">
<xsd:sequence>
<xsd:element name="id" type="xsd:string"/>
<xsd:element name="firstName" type="xsd:string" />
<xsd:element name="lastName" type="xsd:string" />
<xsd:element name="address" type="tns:AddressType"/>
</xsd:sequence>
</xsd:complexType>
But it is not able to parse the ComplexTypes of form:
<xsd:element name="PersonType">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:string"/>
<xsd:element name="firstName" type="xsd:string" />
<xsd:element name="lastName" type="xsd:string" />
<xsd:element name="address" type="tns:AddressType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
In the later form it considers "PersonType" to be just an Element and not ComplexType. Hence the elements included in it are not parsed at all. Does anyone have a solution for this ?
I ran into the same problem today so I thought to see what SOAP-UI has to say about this when providing the wsdl file that has similar contents as the second image,
it showed them as Anonymous Complex Types yet have no solution for them, one thought can be to use the elements separately outside complexType tags.
Also anonymous types are not preferred. Probably the below link can help :
https://www.ibm.com/developerworks/library/ws-avoid-anonymous-types/ws-avoid-anonymous-types-pdf.pdf

refactoring xsd elements with same elements?

I have below xsd elements.
<xsd:element name="requestOne" type="tns:RequestOne"/>
<xsd:complexType name="RequestOne">
<xsd:annotation>
<xsd:documentation>some comment</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="id" type="xsd:string" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="requestTwo" type="tns:RequestTwo"/>
<xsd:complexType name="RequestTwo">
<xsd:annotation>
<xsd:documentation>other comment</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="id" type="xsd:string" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
It has two complex elements but inside them there is duplicated code. How can i refactor above code?
Thanks!
Instead of creating a new element you can use ref to refer to an existing one, tns in the sample below refers to the name space defined by the schema
<xsd:element name="id" type="xsd:string"/>
...
<xsd:complexType name=RequestOne">
<xsd:sequence>
<xsd:element ref="tns:id" minOccurs="1" maxOccurs="1" />
</xsd:sequence>
</xsd:complexType>
...
Though, this does not really reduces the number of characters to write, in that simple case. You might even have a look at how to define abstract types which, similar to object orientated languages, get inherited by children:
<xsd:complexType name="AbstractType" abstract="true">
<xsd:sequence>
<xsd:element name"BaseType">
<xs:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:string" minOccurs="1" maxOccurs="1" >
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
the children now need to inherit from the abstract type in the following manner:
<xsd:complexType name="ChildOne">
<xsd:complexContent>
<xsd:extension base="tns:AbstractType">
<xsd:element name="newElement" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Where newElement is just a new element defined by the child.
Edit: to be more precise, the id element is not really inherited like in an object oriented language but you achieve the same effect. The difference is, that you have to add the base-type within the XML of the concrete realization.
<ChildOne>
<BaseType>
<id>someId</id>
</BaseType>
<!-- elements defined by child one goes here -->
<newElement>new Element</newElement>
</ChildOne>

some noob wsdl questions

<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).

JAXB DOM Node unmarshalling omits elements

I'm unmarshalling some XML from OGC and running into a problem where not all of the elements make it into the final object.
Here is the example XML
<?xml version="1.0"?>
<wfs:CreateStoredQuery xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:fes="http://www.opengis.org/fes/2.0" xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:myns="http://www.someserver.com/myns"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0
http://schemas.opengis.net/wfs/2.0/wfs.xsd"
service="WFS" version="2.0.0">
<wfs:StoredQueryDefinition id="urn:StoredQueries:FeaturesInPolygon">
<wfs:Title>Features In Polygon</wfs:Title>
<wfs:Abstract>Find all the features in a Polygon.</wfs:Abstract>
<wfs:Parameter name="AreaOfInterest" type="gml:PolygonType" />
<wfs:QueryExpressionText returnFeatureTypes="myns:Parks"
language="urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression"
isPrivate="false">
<wfs:Query typeNames="myns:Parks">
<fes:Filter>
<fes:Within>
<fes:ValueReference>geometry</fes:ValueReference>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:posList>0 0 100 0 100 100 0 100 0 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</fes:Within>
</fes:Filter>
</wfs:Query>
</wfs:QueryExpressionText>
</wfs:StoredQueryDefinition>
</wfs:CreateStoredQuery>
Here is the XSD for the offending region
<xsd:complexType name="StoredQueryDescriptionType">
<xsd:sequence>
<xsd:element ref="wfs:Title" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="wfs:Abstract" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="ows:Metadata" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="Parameter"
type="wfs:ParameterExpressionType"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="QueryExpressionText"
type="wfs:QueryExpressionTextType"
minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:anyURI" use="required"/>
</xsd:complexType>
<xsd:complexType name="ParameterExpressionType">
<xsd:sequence>
<xsd:element ref="wfs:Title" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="wfs:Abstract" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="ows:Metadata" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="type" type="xsd:QName" use="required"/>
</xsd:complexType>
<xsd:complexType name="QueryExpressionTextType" mixed="true">
<xsd:choice>
<xsd:any namespace="##other" processContents="skip"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:any namespace="##targetNamespace" processContents="skip"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:choice>
<xsd:attribute name="returnFeatureTypes"
type="wfs:ReturnFeatureTypesListType" use="required"/>
<xsd:attribute name="language" type="xsd:anyURI" use="required"/>
<xsd:attribute name="isPrivate" type="xsd:boolean" default="false"/>
</xsd:complexType>
<xsd:simpleType name="ReturnFeatureTypesListType">
<xsd:list itemType="xsd:QName"/>
</xsd:simpleType>
The part that is messing up is the xsd:any under QueryExpressionTextType. It has the attribute processContents="skip" which causes JAXB to create DOM objects instead of the JAXB classes. I figured I would just use the JAXB context to unmarshall the DOM Nodes afterwards, here is some example code for what I tried.
public static void main(String[] args) throws Exception {
InputStream in = new FileInputStream("test/res/createQuery.xml");
JAXBContext context = JAXBContext.newInstance(
net.opengis.gml.v_3_2_1.ObjectFactory.class,
net.opengis.wfs.v_2_0_0.ObjectFactory.class,
net.opengis.filter.v_2_0_0.ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<?> jElem = (JAXBElement<?>) unmarshaller.unmarshal(in);
CreateStoredQueryType create = (CreateStoredQueryType) jElem.getValue();
List<StoredQueryDescriptionType> descriptions = create
.getStoredQueryDefinition();
StoredQueryDescriptionType desc = descriptions.get(0);
List<QueryExpressionTextType> texts = desc.getQueryExpressionText();
QueryExpressionTextType text = texts.get(0);
List<Object> contents = text.getContent();
Node node = (Node) contents.get(0);
jElem = (JAXBElement<?>) unmarshaller.unmarshal(node);
QueryType query = (QueryType) jElem.getValue();
// prints[{http://www.someserver.com/myns}Parks]
System.out.println(query.getTypeNames());
// is false
System.out.println(query.isSetAbstractSelectionClause());
}
It gets the Query object out just fine (including the typeNames attribute). But it skips the Filter object. The Filter object inherits from AbstractSelectionClause, so it should be set in the returned object, but it isn't. I debugged and checked the DOM Element and confirmed that the Filter is in the DOM tree.
To get the JAXB objects I'm using, I checked out the SVN repository from the jvnet OGC project, I did have to compile the WFS 2.0 schemas, but the Filter and GML schemas were already done.
Any insight would be greatly appreciated.
The example XML came from the OGC WFS 2.0 specification document (09-025r1) on page 81. There is a typo that has the xmlns for Filter as "http://www.opengis.org/fes/2.0" when is should be "http://www.opengis.net/fes/2.0". JAXB was seeing this as an unrecognized element and dropping it on the floor.

Categories