I want to have the following XML
<doc>
<items>
<item />
<item />
<item />
</items>
<things>
<thing />
<thing />
<thing />
</things>
</doc>
The schema would be something like
<schema>
<element name="doc">
<complexType>
<all>
<element name="items" minOccurs="1">
<complexType>
<sequence>
<element name="item" minoccurs="0" maxoccurs="unbounded" />
</sequence>
</complexType>
</element>
<element name="things" minOccurs="1">
<complexType>
<sequence>
<element name="thing" minoccurs="0" maxoccurs="unbounded" />
</sequence>
</complexType>
</element>
</all>
/<complexType>
</element>
</schema>
Now with JAXB it creates a class called Doc which would have an Items type which has an Item. Therefor to add the first element I have to do this.
Doc doc = new Doc();
Items items = new Items();
items.getItem().add(new Item());
doc.setItems(items);
I would like to be able to do the following instead:
Doc doc = new Doc();
doc.getItems().add(new Item());
Or even better:
Doc doc = new Doc();
doc.addItem(new Item());
Anyway of doing this through JAXB bindings?
Per the comment by nsfyn55, there does not seem to be any way to do this with bindings.
However, if you were using JAXB annoations, you could do:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Doc {
#XmlElementWrapper(name = "items")
#XmlElement(name="item")
List<Item> items = new ArrayList<Item>();
public void addItem(Item item) {
this.items.add(item);
}
// ...
}
You need the wrapper tag. It is what defines the elements as a grouped sequence and not just a bunch of arbitrary elements.
I would like to be able to do the following instead:
Doc doc = new Doc();
doc.getItems().add(new Item());
I believe that you can, (at least up to JAXB 2.1) the generated code for getItems() would check for a null member collection, and create it if necessary before returning it. So it always returns a 'live' list object.
Related
What is the best way to convert this XML into Java objects?
<entity>
<customers id=2 other="data">
<customer name="john">testData1</customer>
<customer name="jenny">testData2</customer>
<customer name="joe">testData3</customer>
<customer name="joanna">testData4</customer>
</customers>
</entity>
Is it best to use a custom XMLAdapter with a HashMap to convert multiple xml rows of <customer>? I'm not sure if the XMLAdapter is the proper use case for this scenario. Any ideas would be appreciated.
Since the nesting isn't very deep, you could just have Entity, Customer classes and then use these annotations for the mapping in the entity class:
#XmlElementWrapper(name="customers")
#XmlElement(name="customer")
public void setCustomers(List<Customer> customers) {
this.customers= customers;
}
References:
XmlElementWrapper
Best approach, in my opinion, would be to write an xsd file to validate against your xml. You can use that to generate your java classes using xjc which comes bundled with Java. This should get you there.
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:NameOfNamespace="http://enter.your.namespace.here"
targetNamespace="http://enter.your.namespace.here"
attributeFormDefault="unqualified"
elementFormDefault="qualified">
<complexType name="customer">
<simpleContent>
<extension base="string">
<attribute name="name"/>
</extension>
</simpleContent>
</complexType>
<complexType name="customers">
<sequence>
<element name="customer" type="NameOfNamespace:customer"/>
</sequence>
<attribute name="id" type="positiveInteger"/>
<attribute name="other"/>
</complexType>
<complexType name="entity" >
<sequence>
<element name="customers" type="NameOfNamespace:customers" minOccurs="1" maxOccurs="1"/>
</sequence>
</complexType>
<element name="entity" type="NameOfNamespace:entity"/>
</schema>
Open a command prompt to the folder where you put your xsd file, and then generate java code you'll just need to type:
$ xjc nameOfSchemaFile.xsd
assuming your java 'bin' folder is in your path. The classes generated will be created in folder with the same name as your targetNamespace.
Using these you can follow the instructions in Naimish's example JAXB Hello World Example
My provider must have an array of object as input parameter, according to the WSDL:
<element name="classifica" type="Q4:Titolario" maxOccurs="unbounded" minOccurs="0"/>
This is the generated method:
public void protoModificaProtocollo(...,...,...,Titolario[] classifica,....) {
How to get the input values of this array (always returns null).
EDIT
This is the xsd schema of the method to provide:
<import schemaLocation="TipoVerso.xsd" namespace="http://regione.toscana.it/rfc205/interpro.TipoVerso"/>
<import schemaLocation="Anagrafica.xsd" namespace="http://regione.toscana.it/rfc205/interpro.Anagrafica"/>
<import schemaLocation="Titolario.xsd" namespace="http://regione.toscana.it/rfc205/interpro.Titolario"/>
<element name="protoModificaProtocolloElement" type="tns:protoModificaProtocollo"/>
<complexType name="protoModificaProtocollo">
<sequence>
<element name="numero" type="int" maxOccurs="1" minOccurs="1"/>
<element name="anno" type="int" maxOccurs="1" minOccurs="1"/>
<element name="verso" type="Q1:TipoVerso" maxOccurs="1" minOccurs="1"/>
<element name="oggetto" type="string" maxOccurs="1" minOccurs="0"/>
<element name="classifica" type="Q4:Titolario" maxOccurs="unbounded" minOccurs="0"/>
<element name="ufficio" type="Q2:Anagrafica" maxOccurs="unbounded" minOccurs="0"/>
</sequence>
</complexType>
and this is the xsd schema of Titolario
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://regione.toscana.it/rfc205/interpro.Titolario"
elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://regione.toscana.it/rfc205/interpro.Titolario">
<complexType name="Titolario">
<sequence>
<element name="codice" type="string" maxOccurs="1" minOccurs="1"></element>
<element name="descrizione" type="string" maxOccurs="1" minOccurs="0">
</element>
</sequence>
</complexType>
</schema>
Here the SOAP message sent:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:int="http://regione.toscana.it/rfc205/interpro"
xmlns:int1="http://regione.toscana.it/rfc205/interpro.protoModificaProtocollo"
xmlns:int2="http://regione.toscana.it/rfc205/interpro.Titolario"
xmlns:int3="http://regione.toscana.it/rfc205/interpro.Anagrafica">
<soapenv:Header/>
<soapenv:Body>
<int:protoModificaProtocollo>
<int1:numero>140</int1:numero>
<int1:anno>2014</int1:anno>
<int1:verso>P</int1:verso>
<!--Optional:-->
<int1:oggetto>test</int1:oggetto>
<!--Zero or more repetitions:-->
<int1:classifica>
<int2:codice>34</int2:codice>
<!--Optional:-->
<int2:descrizione>test description</int2:descrizione>
</int1:classifica>
</int:protoModificaProtocollo>
</soapenv:Body>
</soapenv:Envelope>
In the provider, this is the method:
public void protoModificaProtocollo(int numero, int anno, TipoVerso verso,
String oggetto, Titolario[] classificazione, Anagrafica[] ufficio,
ResponseProtocolloHolder protocollo_resp, ResponseErrorHolder response_msg_err) {
... some stuff here ...
System.out.println("getCodice():" + classificazione[0].getCodice()); <-- THIS LINE ALWAYS RETURNS NULL
Notice that, in input parameters, if I change
Titolario[] classificazione
with
Titolario classificazione
my debug line prints:
34
UPDATE 2
TIA Simon, here pastebin you can find the full WSDL. And here Titolario.java the class for Titolario. I've noticed that when consumer call provider, Titolario constructor i call N times, depending of number of Titolario occurrences in SOAP request. As you can see, the constructor is an empty constructor.
In this moment, my protocol is RPC/encoded, just becouse i've must understand the problem reported here Literal vs Encoded (if you please can take a look also at this...:-))
I've found some references about the problem, for example this article, maybe related to mine.
Anyway, i would like to know if there is a Java workaround to manage this. Thanks again!
Found the problem!
The fact was that i've used the same given WSDL both to generate the provider and to create the SoapUI project.
Basically the trick was:
generate the provider in Domino using the given WSDL
set RPC/Literal, according to the <soap:body use="literal"/> directive
export the Domino WSDL of the provider
create the SoapUI project with it
use it to call services provided
That's it! ... a very stupid mistake! :-(
Without a full WSDL I can't say with 100% this is the issue.
Based on what you have so far.
1. There are known issues where the "xsd:string" is wrapped with custom string handler. I have see references before where "string" would just sit on top of "xsd:string". So I would start with changing that back to "xsd:string" and see if it helps.
2. As your web service provider has the following:
Titolario[] classifica
Normally your WSDL should create an xxxArray Object which your consumer sends, and not the array object itself. (Tested on Doc/Literal + RPC/Literal, as "minoccurs" only shows up on Literal).
For example:
public String TestProtoModificaProtocollo (Titolario x[]) { ...
Converts to:
<complexType name="Titolario">
<sequence>
<element name="codice" nillable="true" type="xsd:string"/>
<element maxOccurs="unbounded" minOccurs="0" name="descrizione" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="TitolarioArray">
<sequence>
<element maxOccurs="unbounded" minOccurs="0" name="item" type="impl:Titolario"/>
</sequence>
</complexType>
<element name="x" type="impl:TitolarioArray"/>
<element name="TestProtoModificaProtocolloReturn" type="xsd:string"/>
So your consumer would translate to:
public java.lang.String testProtoModificaProtocollo(TitolarioArray x) throws java.rmi.RemoteException;
I am using JAXB in the XSD / XML to Java direction. My XSD contains derived types and the XML I have to unmarshall contain xsi:type attributes. Looking at the code JAXB (the default Sun implementation) has generated (for the unmarshalling) there seems to appear no methods to get these attributes.
Is it because I can always do a getClass() on the unmarshalled Java object and find the actual class?
Still, would it not be the case that depending on the packages or classes I provide to the JAXBContext.newInstance some base class may be instantiated instead? (one that is a superclass of the class corresponding to the actual value of the xsi:type attribute). In such a case being able to read the value of the actual attribute as it appears in the XML instance could be needed.
The JAXB (JSR-222) implementation will take care of everything for you. JAXB considers that each class corresponds to a complex type. It has an algorithm for figuring out the type name, but you can override this using the #XmlType annotation. When an element is unmarshalled if it contains an xsi:type attribute then JAXB will look to see if there is a class associated with that type. If there is it will instantiate a class of that type, if not it will instantiate the type that corresponds to that element based on the mapping metadata supplied via annotations.
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
UPDATE
Below is an example that may help:
schema.xsd
In the XML schema below the complex type canadianAddress extends the complexType address.
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/customer"
xmlns:tns="http://www.example.org/customer"
elementFormDefault="qualified">
<element name="customer">
<complexType>
<sequence>
<element name="address" type="tns:address"/>
</sequence>
</complexType>
</element>
<complexType name="address">
<sequence>
<element name="street" type="string"/>
</sequence>
</complexType>
<complexType name="canadianAddress">
<complexContent>
<extension base="tns:address">
<sequence>
<element name="postalCode" type="string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
Demo
In the demo code below the XML will be converted to the JAXB model generated from the above XML schema, and then converted back to XML.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance("org.example.customer");
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/org/example/customer/input.xml");
Customer customer = (Customer) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
input.xml/Output
Below is the XML. The address element is qualified with xsi:type to indicate that it holds an instance of canadianAddress instead of just an address.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns="http://www.example.org/customer">
<address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="canadianAddress">
<street>1 A Street</street>
<postalCode>Ontario</postalCode>
</address>
</customer>
<complexType name="spThread">
<sequence>
<element name="SPThreadID" type="int" />
<element name="durtime" minOccurs="0" default="0">
<simpleType>
<restriction base="int">
<minInclusive value="0" />
</restriction>
</simpleType>
</element>
<element name="minexecutions" minOccurs="0" default="0">
<simpleType>
<restriction base="int">
<minInclusive value="0" />
</restriction>
</simpleType>
</element>
<element name="numThreads" type="int" />
<element name="procedures" type="spm:procedure" minOccurs="1"
maxOccurs="unbounded" />
</sequence>
</complexType>
i want to generate this type of .xsd file using java code..? How can i do that.?
Specially how to generate Simple type elements and put restrictions on it ?
Instead of creating your own simple type to represent integers starting with 0, you could leverage the existing xs:nonNegativeInteger type. I'll demonstrate with an example.
SpThread
You can use the #XmlSchemaType annotation to specify what type should be generated in the XML schema for a field/property.
package forum11667335;
import javax.xml.bind.annotation.XmlSchemaType;
public class SpThread {
private int durTime;
#XmlSchemaType(name="nonNegativeInteger")
public int getDurTime() {
return durTime;
}
public void setDurTime(int durTime) {
this.durTime = durTime;
}
}
Demo
You can use the generateSchema method on JAXBContext to generate an XML schema:
package forum11667335;
import java.io.IOException;
import javax.xml.bind.*;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SpThread.class);
jc.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
StreamResult result = new StreamResult(System.out);
result.setSystemId(suggestedFileName);
return result;
}
});
}
}
Output
Below is the XML schema that was generated.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="spThread">
<xs:sequence>
<xs:element name="durTime" type="xs:nonNegativeInteger"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
You can use any XML-handling API to achieve this. JDOM is one of them. If you'd like an API specific to building XML Schemas which you then serialize into XML, you might want to check out Eclipse MDT API.
You can use Java2Schema tool for generating schema from java classes, and also you can try JaxB 2.0
I recommend you JAXB to any XML jobs you do. But normally XSD files are generated manually and then XML files are generated programatically using the XSD files. What are you trying to develop?
I am trying to consume a Java Webservice using Visual Basic.net. I am getting an error on deserialization "Cannot assign object of type System.Xml.XmlNode[] to an object of type System.String".
What I have been reading is that the wsdl specification may not conform to the WS-I BP 1.1 specification. Link Here
The other solution talked about here is to change the response xml to conform to a .net datatype. I have tried to do this for other reasons a couple of years back and it proved to be unstable.
A snippet of the wsdl is this:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.interfaces.sessions.APILink.amdocs"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://ws.interfaces.sessions.APILink.amdocs"
xmlns:intf="http://ws.interfaces.sessions.APILink.amdocs"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns2="http://io.datainfo.APILink.amdocs"
xmlns:tns3="http://datainfo.APILink.amdocs"
xmlns:tns4="http://awsi.amdocs.com"
xmlns:tns5="http://exceptions.APILink.amdocs"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<complexType name="InPersonInfo">
<complexContent>
<extension base="tns2:BaseInputOutputInfo">
<sequence>
<element maxOccurs="1" minOccurs="0" name="mItem1" type="xsd:int"/>
<element maxOccurs="1" minOccurs="0" name="mID" nillable="true" type="xsd:string"/>
<element maxOccurs="1" minOccurs="0" name="mPersonType" type="tns4:char"/>
</sequence>
</extension>
</complexContent>
</complexType>
The other pieces from the WSDL I am seeing is just the message and operation sections.
EDIT 2011-04-21: This question mentions what I am going through.
I am not familiar with java what I know is that it is being created/consumed with AXIS or SOAPUI and somehow it is not creating the WSDL according to standard and there is my problem. If I find an answer to solve it I will post it here.