I am a bit lost at this point. I am by no means a SOAP/JAXb expert, however, I am trying to create a generic class that will marshal/call/unmarshal for any service. I am using the Weather Service wsdl as a starting point to prove out the concept.
I have finally gotten the marshalling, call and unmarshalling to execute without error, however, the response object is not being populated. Can anyone assist in identifying what I am doing incorrectly? I am also looking for a good explanation to the answer if possible so I can learn from this experience.
Again, there is no error while excuting. The issue is that the value of GetCityWeatherByZIPResponse.GetCityWeatherByZIPResult comes out to be null. I know the document is returning the correct results as the result printout is as follows:
Result printout:
<?xml version="1.0" encoding="UTF-8"?><GetCityWeatherByZIPResponse xmlns="http://ws.cdyne.com/WeatherWS/">
<GetCityWeatherByZIPResult>
<Success>true</Success>
<ResponseText>City Found</ResponseText>
<State>MO</State>
<City>Saint Charles</City>
<WeatherStationCity>Farmington</WeatherStationCity>
<WeatherID>4</WeatherID>
<Description>Sunny</Description>
<Temperature>79</Temperature>
<RelativeHumidity>47</RelativeHumidity>
<Wind>CALM</Wind>
<Pressure>30.00S</Pressure>
<Visibility/>
<WindChill/>
<Remarks/>
</GetCityWeatherByZIPResult>
</GetCityWeatherByZIPResponse>
Response: GetCityWeatherByZIPResult: null
Test Web Service:
http://wsf.cdyne.com/WeatherWS/Weather.asmx
Initial call (done via JBehave):
#Given("I call the weather soap service")
public void givenICallTheWeatherSoapService() {
GetCityWeatherByZIP weather = new GetCityWeatherByZIP();
weather.setZIP("63304");
try {
new WeatherTools();
WeatherSoap weatherSoap = new WeatherSoap();
GetCityWeatherByZIPResponse response = weatherSoap.getCityWeatherByZip("63304");
System.out.println("Response: " + response);
} catch (JAXBException | ParserConfigurationException | SOAPException | IOException e) {
Assert.fail(e.getMessage());
}
}
Soap Service Class:
public class WeatherSoap extends PTFSoapClient {
public WeatherSoap() throws JAXBException, ParserConfigurationException, SOAPException {
super(PTFApplication.getConfig(Environment.executionEnv.getEnv(), "Weather SOAP endpoint"));
}
public GetCityWeatherByZIPResponse getCityWeatherByZip(String zip) throws JAXBException, SOAPException, IOException {
GetCityWeatherByZIP weatherByZip = new GetCityWeatherByZIP();
weatherByZip.setZIP(zip);
try {
sendRequest(weatherByZip);
return (GetCityWeatherByZIPResponse) unmarshallResponse(GetCityWeatherByZIPResponse.class);
} catch (ParserConfigurationException | XMLStreamException e) {
e.printStackTrace();
return null;
}
}
}
Base Framework Class genericizing the call (usable for all SOAP calls):
public class PTFSoapClient {
private JAXBContext context;
private Marshaller marshaller;
private Object object;
private SOAPMessage message;
private String endpoint;
private SOAPMessage response;
public PTFSoapClient(String endpoint) {
this.endpoint = endpoint;
}
public void toConsole() throws JAXBException, SOAPException, IOException {
message.writeTo(System.out);
System.out.print("\n");
}
public SOAPMessage sendRequest(Object obj) throws JAXBException, ParserConfigurationException, SOAPException {
object = obj;
context = JAXBContext.newInstance(obj.getClass());
marshaller = context.createMarshaller();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();
marshaller.marshal(object,doc);
MessageFactory factory = MessageFactory.newInstance();
message = factory.createMessage();
message.getSOAPBody().addDocument(doc);
message.saveChanges();
SOAPConnection connection = SOAPConnectionFactory.newInstance().createConnection();
response = connection.call(message, endpoint);
connection.close();
try {
System.out.println("Response:");
response.writeTo(System.out);
System.out.println("");
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
public Object unmarshallResponse(Class<?> classname) throws JAXBException, XMLStreamException, SOAPException, IOException {
Document doc = response.getSOAPBody().extractContentAsDocument();
try {
System.out.println("Document: ");
printDocument(doc, System.out);
System.out.println("");
} catch (TransformerException e) {
e.printStackTrace();
}
Unmarshaller unmarshaller = JAXBContext.newInstance(classname).createUnmarshaller();
return unmarshaller.unmarshal(doc);
}
public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc),
new StreamResult(new OutputStreamWriter(out, "UTF-8")));
}
}
Base unmarshal object:
#XmlRootElement(name = "GetCityWeatherByZIPResponse",
namespace = "http://ws.cdyne.com/WeatherWS/")
public class GetCityWeatherByZIPResponse {
GetCityWeatherByZIPResult GetCityWeatherByZIPResult;
public GetCityWeatherByZIPResult getGetCityWeatherByZIPResult() {
return GetCityWeatherByZIPResult;
}
public void setGetCityWeatherByZIPResult(GetCityWeatherByZIPResult GetCityWeatherByZIPResult) {
this.GetCityWeatherByZIPResult = GetCityWeatherByZIPResult;
}
#Override
public String toString() {
return "GetCityWeatherByZIPResult: " + GetCityWeatherByZIPResult;
}
}
Sub umarshal object:
public class GetCityWeatherByZIPResult {
boolean Success;
String ResponseText;
String State;
String City;
String WeatherStationCity;
String WeatherID;
String Description;
int Temperature;
int RelativeHumidity;
String Wind;
String Pressure;
String Visibility;
String WindChill;
String Remarks;
public boolean isSuccess() {
return Success;
}
public void setSuccess(boolean success) {
Success = success;
}
public String getResponseText() {
return ResponseText;
}
public void setResponseText(String responseText) {
ResponseText = responseText;
}
public String getState() {
return State;
}
public void setState(String state) {
State = state;
}
public String getCity() {
return City;
}
public void setCity(String city) {
City = city;
}
public String getWeatherStationCity() {
return WeatherStationCity;
}
public void setWeatherStationCity(String weatherStationCity) {
WeatherStationCity = weatherStationCity;
}
public String getWeatherID() {
return WeatherID;
}
public void setWeatherID(String weatherID) {
WeatherID = weatherID;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
}
public int getTemperature() {
return Temperature;
}
public void setTemperature(int temperature) {
Temperature = temperature;
}
public int getRelativeHumidity() {
return RelativeHumidity;
}
public void setRelativeHumidity(int relativeHumidity) {
RelativeHumidity = relativeHumidity;
}
public String getWind() {
return Wind;
}
public void setWind(String wind) {
Wind = wind;
}
public String getPressure() {
return Pressure;
}
public void setPressure(String pressure) {
Pressure = pressure;
}
public String getVisibility() {
return Visibility;
}
public void setVisibility(String visibility) {
Visibility = visibility;
}
public String getWindChill() {
return WindChill;
}
public void setWindChill(String windChill) {
WindChill = windChill;
}
public String getRemarks() {
return Remarks;
}
public void setRemarks(String remarks) {
Remarks = remarks;
}
}
Your Current Mapping
When you specify the namespace property on the #XmlRootElement annotation, it only applies to that one element.
#XmlRootElement(name = "GetCityWeatherByZIPResponse",
namespace = "http://ws.cdyne.com/WeatherWS/")
public class GetCityWeatherByZIPResponse {
Your XML Document
Your XML document specifies a default namespace. This means that all elements without another explicit namespace mapping are also part of the http://ws.cdyne.com/WeatherWS/ namespace.
<?xml version="1.0" encoding="UTF-8"?><GetCityWeatherByZIPResponse xmlns="http://ws.cdyne.com/WeatherWS/">
<GetCityWeatherByZIPResult>
<Success>true</Success>
The Namespace Fix
You are going to want to specify the namespace mapping at the package level so that it applies to all your element mappings. This is done using the package level #XmlSchema annotation on a speciial class called package-info.
#XmlSchema(
namespace = "http://ws.cdyne.com/WeatherWS/",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
I have written more about JAXB and namespace qualification on my blog:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Update
Default Element Names
The default elements for your properties don't match your XML. for the property below the expected element name will be getCityWeatherByZIPResult so you will need to override the default using the #XmlElement annotation.
#XmlElement(name="GetCityWeatherByZIPResult")
public GetCityWeatherByZIPResult getGetCityWeatherByZIPResult() {
return GetCityWeatherByZIPResult;
}
Debugging Tip
When you encounter problems unmarshalling, populate your object model and marshal it to see what the expected XML is based on your current mappings.
Related
I need to load an XML files, but there exists two identical formats of the file, save for the namespace being different - in my simplified example,
apple:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
pear:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
The XmlRootElement references a specific namespace, and so I can't process both files the same way:
public class NamespaceTest {
#XmlRootElement(namespace = "apple")
public static class Container {
}
public static void main(final String[] args) throws Exception {
// Correct namespace - works
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
""");
// Incorrect namespace - doesn't work
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
""");
}
private static void unmarshall(final String xml) throws Exception {
try (Reader reader = new StringReader(xml)) {
System.out.println(JAXBContext.newInstance(Container.class).createUnmarshaller().unmarshal(reader));
}
}
}
}
Gives the output:
com.my.app.NameSpaceTest$Container#77167fb7
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"pear", local:"container"). Expected elements are <{apple}container>
At the moment I've got this working in a sub-optimal way by modifying the data as it's being read, using https://stackoverflow.com/a/50800021 - but I'd like to move this into JAXB if possible.
public class NameSpaceTest {
#XmlRootElement(namespace = "apple")
public static class Container {
}
public static void main(final String[] args) throws Exception {
// Correct namespace
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
""");
// Incorrect namespace
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
""");
}
private static void unmarshall(final String xml) throws Exception {
try (Reader reader = new TranslatingReader(new BufferedReader(new StringReader(xml))) {
#Override
public String translate(final String line) {
return line.replace("pear", "apple");
}
}) {
System.out.println(JAXBContext.newInstance(Container.class).createUnmarshaller().unmarshal(reader));
}
}
/** #see Source */
private abstract static class TranslatingReader extends Reader {
private final BufferedReader input;
private StringReader output = new StringReader("");
public TranslatingReader(final BufferedReader input) {
this.input = input;
}
public abstract String translate(final String line);
#Override
public int read(final char[] cbuf, int off, int len) throws IOException {
int read = 0;
while (len > 0) {
final int nchars = output.read(cbuf, off, len);
if (nchars == -1) {
final String line = input.readLine();
if (line == null) {
break;
} else {
output = new StringReader(translate(line) + System.lineSeparator());
}
} else {
read += nchars;
off += nchars;
len -= nchars;
}
}
if (read == 0) {
read = -1;
}
return read;
}
#Override
public void close() throws IOException {
input.close();
output.close();
}
}
}
Output:
com.my.app.NameSpaceTest$Container#6ce139a4
com.my.app.NameSpaceTest$Container#18ce0030
Assumptions
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jaxb-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>3.0.2</version> <!-- latest, depends on jakarta.xml.bind:jakarta.xml.bind-api:3.0.1 -->
</dependency>
</dependencies>
</project>
OOP-Solution
We abstract (public) Container, and introduce (private or the visibility of our choice(, empty)) implementations to it, with correct qName:
public class NamespaceTest {
public static interface Container {
}
#XmlRootElement(namespace = "apple", name = "container")
private static class ContainerApple implements Container {
}
#XmlRootElement(namespace = "pear", name = "container")
private static class ContainerPear implements Container {
}
...
..!
With identical main method, unmarshall would (still) look like:
...
private static void unmarshall(final String xml) throws Exception {
Unmarshaller umler = CTXT.createUnmarshaller();
try ( Reader reader = new StringReader(xml)) {
System.out.println(umler.unmarshal(reader)
);
}
}
private static final JAXBContext CTXT = initContext();
private static JAXBContext initContext() {
try {
return JAXBContext.newInstance(ContainerApple.class, ContainerPear.class);
} catch (JAXBException ex) {
throw new IllegalStateException("Could not initialize jaxb context.");
}
}
}
Singleton JAXBContext.
(static) Initialization with:
catch exception and re-throw (runtime/unchecked).
all (known) jaxb classes/packages/context(configs).
Prints Us:
com.example.jaxb.test.NamespaceTest$ContainerApple#4493d195
com.example.jaxb.test.NamespaceTest$ContainerPear#2781e022
Filtering the data as it's being read is the right approach (JAXB, or data binding in general, isn't an ideal technology choice if you have to handle versions and variants of the vocabulary). But filter it using a SAX filter, not at the stream level.
Alternatively, normalise the data using an XSLT transformation before processing it using JAXB.
One option is to use a custom org.xml.sax.ContentHandler that rewrites the sax events for namespaces before it delegates to the "normal" Content Handler for jaxb.
Here a self contained example:
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
public class JaxbSaxRewriteNamespaceExample {
#XmlRootElement(name = "container", namespace = "apple")
#XmlAccessorType(XmlAccessType.NONE)
static class Container {
#XmlAttribute(namespace = "apple")
private String attribute;
#XmlElement(namespace = "apple")
private String element;
public String getAttribute() {
return attribute;
}
public String getElement() {
return element;
}
}
public static void main(String[] args) throws Exception {
String orangeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n"
+ "<ns2:container xmlns:ns2=\"apple\" ns2:attribute=\"oranges\"><ns2:element>Orange Element</ns2:element>\r\n"
+ "</ns2:container>";
String appleXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n"
+ "<ns2:container xmlns:ns2=\"apple\" ns2:attribute=\"apples\"><ns2:element>Apple Element</ns2:element>\r\n"
+ "</ns2:container>";
JAXBContext jc = JAXBContext.newInstance(Container.class);
Container orange = read(jc, orangeXml, Collections.singletonMap("orange", "apple"));
Container apple = read(jc, appleXml, Collections.emptyMap());
System.out.println(orange.getAttribute());
System.out.println(orange.getElement());
System.out.println(apple.getAttribute());
System.out.println(apple.getElement());
}
private static Container read(JAXBContext jc, String xml, Map<String, String> namespaceMapping) throws Exception {
UnmarshallerHandler unmarshallerHandler = jc.createUnmarshaller().getUnmarshallerHandler();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true); // Make sure sax parser is namespace aware
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
// Wrap the Jaxb ContentHandler with the custome NamespaceRenamer
xr.setContentHandler(new RenameNamespaceContentHandler(unmarshallerHandler, namespaceMapping));
// See javadoc of InputSource for more options to pass in data, e.g. InputStream
InputSource inputSource = new InputSource(new StringReader(xml)); //
xr.parse(inputSource);
return (Container) unmarshallerHandler.getResult();
}
public static class RenameNamespaceContentHandler implements ContentHandler {
private final ContentHandler delegate;
private final Map<String, String> namespaceMapping;
public RenameNamespaceContentHandler(ContentHandler delegate, Map<String, String> namespaceMapping) {
this.delegate = delegate;
this.namespaceMapping = namespaceMapping;
}
#Override
public void setDocumentLocator(Locator locator) {
delegate.setDocumentLocator(locator);
}
#Override
public void startDocument() throws SAXException {
delegate.startDocument();
}
#Override
public void endDocument() throws SAXException {
delegate.endDocument();
}
#Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if (namespaceMapping.containsKey(uri)) {
delegate.startPrefixMapping(prefix, namespaceMapping.get(uri));
}
delegate.startPrefixMapping(prefix, uri);
}
#Override
public void endPrefixMapping(String prefix) throws SAXException {
delegate.endPrefixMapping(prefix);
}
#Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
delegate.startElement(uri, localName, qName, atts);
}
#Override
public void endElement(String uri, String localName, String qName) throws SAXException {
delegate.endElement(uri, localName, qName);
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
delegate.characters(ch, start, length);
}
#Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
delegate.ignorableWhitespace(ch, start, length);
}
#Override
public void processingInstruction(String target, String data) throws SAXException {
delegate.processingInstruction(target, data);
}
#Override
public void skippedEntity(String name) throws SAXException {
delegate.skippedEntity(name);
}
}
}
I'm trying to implement an xsd validator for schemas, that using imports and includes. I took this answer for example.
Here is my validating method:
public void validate(String filePath, String schemaName) throws Exception
{
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver(new ResourceResolver());
Source schemaFile = new StreamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.setErrorHandler(new MySAXParseErrorHandler());
validator.validate(new StreamSource(filePath));
}
LSInput (entirely the same):
public class LSInputImpl implements LSInput
{
private String publicId;
private String systemId;
public String getPublicId()
{
return publicId;
}
public void setPublicId(String publicId)
{
this.publicId = publicId;
}
public String getBaseURI()
{
return null;
}
public InputStream getByteStream()
{
return null;
}
public boolean getCertifiedText()
{
return false;
}
public Reader getCharacterStream()
{
return null;
}
public String getEncoding()
{
return null;
}
public String getStringData()
{
synchronized (inputStream) {
try {
byte[] input = new byte[inputStream.available()];
inputStream.read(input);
String contents = new String(input);
return contents;
} catch (IOException e) {
e.printStackTrace();
System.out.println("Exception " + e);
}
return null;
}
}
public void setBaseURI(String baseURI)
{
}
public void setByteStream(InputStream byteStream)
{
}
public void setCertifiedText(boolean certifiedText)
{
}
public void setCharacterStream(Reader characterStream)
{
}
public void setEncoding(String encoding)
{
}
public void setStringData(String stringData)
{
}
public String getSystemId()
{
return systemId;
}
public void setSystemId(String systemId)
{
this.systemId = systemId;
}
public BufferedInputStream getInputStream()
{
return inputStream;
}
public void setInputStream(BufferedInputStream inputStream)
{
this.inputStream = inputStream;
}
private BufferedInputStream inputStream;
public LSInputImpl(String publicId, String sysId, InputStream input)
{
this.publicId = publicId;
this.systemId = sysId;
this.inputStream = new BufferedInputStream(input);
}
}
ResourceResolver:
public class ResourceResolver implements LSResourceResolver
{
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(systemId);
return new LSInputImpl(publicId, systemId, resourceAsStream);
}
}
When I'm trying to validate xml with xsd that has only imports, it works fine. But when I split one schema with two by using include, process fails with IOException:
java.io.IOException: Stream closed
at java.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:159)
at java.io.BufferedInputStream.available(BufferedInputStream.java:410)
at LSInputImpl.getStringData(LSInputImpl.java:57)
at com.sun.org.apache.xerces.internal.util.DOMEntityResolverWrapper.resolveEntity(DOMEntityResolverWrapper.java:130)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.resolveEntity(XMLEntityManager.java:1073)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.resolveDocument(XMLSchemaLoader.java:659)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.resolveSchemaSource(XSDHandler.java:2105)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1088)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1120)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:620)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
org.xml.sax.SAXParseException; systemId: file:///C:/Users/adobryn/Java/sub/schema.xsd; lineNumber: 456; columnNumber: 73; src-resolve: Cannot resolve the name 'st_Term' to a(n) 'type definition' component.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4162)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4145)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1741)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseNamedElement(XSDElementTraverser.java:405)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseLocal(XSDElementTraverser.java:194)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseLocalElements(XSDHandler.java:3618)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:633)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Schema with include:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/schema" targetNamespace="http://www.w3.org/schema" elementFormDefault="qualified">
<xsd:include schemaLocation="st_Term.xsd"/>
<xsd:element name="Term" type="st_Term" minOccurs="0" maxOccurs="1"/>
</xsd:schema>
I think, there is something wrong with getStringData() method. How can I modify it? Thanks for your help!
The problem was, that included xsd also has an include, and ResourceResolver looked in resources folder for it, not resources/sub, where that subincluded xsd was. So, I took another example, that has path tracking, and it works now :) Please note, that schemaBasePath should start with "/"
I had the same problem with includes in .xsd several years ago.
As I can remember my problem was that .xsd resource and its includes were located in some package (directory) within resources.
I've solved it with the following ResourceResolver where the package is passed to constructor (resourceRoot argument):
class ResourceResolver implements LSResourceResolver {
private String resourceRoot;
public ResourceResolver(String resourceRoot) {
this.resourceRoot = resourceRoot;
}
public LSInput resolveResource(String type, String namespaceURI,String publicId, String systemId, String baseURI) {
InputStream resourceAsStream = this.getClass().getResourceAsStream(resourceRoot + "/" + systemId);
return new Input(publicId, systemId, resourceAsStream);
}
}
and Input:
static class Input implements LSInput {
private String publicId;
private String systemId;
public String getPublicId() {
return publicId;
}
public void setPublicId(String publicId) {
this.publicId = publicId;
}
public String getBaseURI() {
return null;
}
public InputStream getByteStream() {
return null;
}
public boolean getCertifiedText() {
return false;
}
public Reader getCharacterStream() {
return null;
}
public String getEncoding() {
return null;
}
public String getStringData() {
synchronized (inputStream) {
try {
byte[] input = new byte[inputStream.available()];
inputStream.read(input);
String contents = new String(input, "UTF-8");
return contents;
} catch (IOException e) {
e.printStackTrace();
System.out.println("Exception " + e);
return null;
}
}
}
public void setBaseURI(String baseURI) {
}
public void setByteStream(InputStream byteStream) {
}
public void setCertifiedText(boolean certifiedText) {
}
public void setCharacterStream(Reader characterStream) {
}
public void setEncoding(String encoding) {
}
public void setStringData(String stringData) {
}
public String getSystemId() {
return systemId;
}
public void setSystemId(String systemId) {
this.systemId = systemId;
}
private final BufferedInputStream inputStream;
public Input(String publicId, String sysId, InputStream input) {
this.publicId = publicId;
this.systemId = sysId;
this.inputStream = new BufferedInputStream(input);
}
}
I'm newbie in XML parsing and try to understand JAXB. Have the following task:
Implemented the following method, to got the személy object by id parameter,but it returns null:
public Személy getSzemélyById(String id) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Személy.class);
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
XMLReader xmlReader = spf.newSAXParser().getXMLReader();
InputSource inputSource;
inputSource = new InputSource(new FileReader("C:\\Users\\zbocskay.TS-EU\\Documents\\NetBeansProjects\\prt2014levzh\\people.xml"));
SAXSource source = new SAXSource(xmlReader, inputSource);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Személy személy = (Személy) unmarshaller.unmarshal(source);
System.out.println(személy.toString());
return new Személy(személy.getId(), személy.getVezetéknév(), személy.getKeresztnév(), személy.getÉletkor(), személy.getCím(), személy.státusz.DIÁK);
} catch (JAXBException e) {
} catch (FileNotFoundException | ParserConfigurationException ex) {
Logger.getLogger(SzemélyDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXNotRecognizedException ex) {
Logger.getLogger(SzemélyDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXNotSupportedException ex) {
Logger.getLogger(SzemélyDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(SzemélyDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
Here is Személy Class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Személy {
public enum Státusz {
FELNŐTT, DIÁK, NYUGDÍJAS, GYERMEK
}
#XmlElement
protected String id;
#XmlElement
protected String vezetéknév;
#XmlElement
protected String keresztnév;
protected Integer életkor;
#XmlElement
protected String cím;
protected Státusz státusz;
public Személy(String id) {
super();
this.id = id;
}
public void setÉletkor(Integer életkor) {
this.életkor = életkor;
}
public String getVezetéknév() {
return vezetéknév;
}
public void setVezetéknév(String vezetéknév) {
this.vezetéknév = vezetéknév;
}
public String getKeresztnév() {
return keresztnév;
}
public void setKeresztnév(String keresztnév) {
this.keresztnév = keresztnév;
}
public String getCím() {
return cím;
}
public void setCím(String cím) {
this.cím = cím;
}
public String getId() {
return id;
}
public Személy(String id, String vezetéknév, String keresztnév,
Integer életkor, String cím, Státusz státusz) {
this(id);
this.vezetéknév = vezetéknév;
this.keresztnév = keresztnév;
this.életkor = életkor;
this.cím = cím;
this.státusz = státusz;
}
public Személy(String id, String vezetéknév, String keresztnév,
String születésiDátum, String cím, String diákigazolványszám,
Státusz státusz) throws ParseException {
this(id);
this.vezetéknév = vezetéknév;
this.keresztnév = keresztnév;
this.életkor = meghatározÉletkort(születésiDátum);
this.cím = cím;
this.státusz = státusz;
}
public Integer getÉletkor() {
return életkor;
}
#Override
public String toString() {
return "Személy [id=" + id + ", vezetéknév=" + vezetéknév
+ ", keresztnév=" + keresztnév + ", életkor=" + életkor
+ ", cím=" + cím + ", státusz=" + státusz + "]";
}
}
And people.xml file with data:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE személyek SYSTEM "people.dtd">
<személyek>
<személy id="micimacko">
<vezetéknév>Mici</vezetéknév>
<keresztnév>Mackó</keresztnév>
<születésidátum>1921.08.21</születésidátum>
<cím>Százholdas Pagony</cím>
<fotó>http://upload.wikimedia.org/wikipedia/en/1/10/Winniethepooh.png
</fotó>
</személy>
</személyek>
Running in the main class by the following:
public static void main(String args[]) {
SzemélyDAO ddd = new SzemélyDAOImpl();
System.out.println(ddd.getSzemélyById("micimacko"));
}
Could anybody help me, what am I doing wrong?Thanks.
As the exception states you need to add a no-arg constructor to your Személy class. This means a constructor that doesn't take any parameters. Currently the class has a single constructor that takes a String. Adding the following would work:
private Személy() {
}
I'm attempting to following this example of SAX XML Parser: How to parse XML using the SAX parser
However when debugging - some items are returning null. I have verified they are contained within the XML file I am connecting to - but I believe I've set my getVideos/setVideos parameters incorrectly and I'm not sure how they might be corrected.
XML Data
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<response>
<cmd>getVideos</cmd>
<success>1</success>
<NumberOfVideos>4</NumberOfVideos>
<Videos>
<Video>
<VideoName>sample_iPod</VideoName>
<VideoDesc/>
<VideoUrl>
http://mobile.example.com/omv/wp-content/uploads/sites/6/2014/01/omv/1/06087297988b.m4v
</VideoUrl>
<VideoTags/>
</Video>
<Video>
<VideoName>sample_mpeg4</VideoName>
<VideoDesc/>
<VideoUrl>
http://mobile.example.com/omv/wp-content/uploads/sites/6/2014/01/omv/1/b5ed9e7100e2.mp4
</VideoUrl>
<VideoTags/>
</Video>
<Video>
<VideoName>sample_sorenson</VideoName>
<VideoDesc/>
<VideoUrl>
http://mobile.example.com/omv/wp-content/uploads/sites/6/2014/01/omv/1/2a8e64b24997.mov
</VideoUrl>
<VideoTags/>
</Video>
<Video>
<VideoName>sample_iTunes</VideoName>
<VideoDesc/>
<VideoUrl>
http://mobile.example.com/omv/wp-content/uploads/sites/6/2014/01/omv/1/6c7f65254aad.mov
</VideoUrl>
<VideoTags/>
</Video>
</Videos>
</response>
SAX Parser Related Java:
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (qName.equalsIgnoreCase("response")) {
// add it to the list
response.add(cmd);
} else if (qName.equalsIgnoreCase("success")) {
cmd.setSuccess(tempVal);
} else if (qName.equalsIgnoreCase("numberofvideos")) {
cmd.setNumberOfVideos(tempVal);
} else if (qName.equalsIgnoreCase("videos")) {
cmd.setVideos(tempVal);
} else if (qName.equalsIgnoreCase("video")) {
cmd.setVideo(tempVal);
} else if (qName.equalsIgnoreCase("videoname")) {
cmd.setVideoName(tempVal);
} else if (qName.equalsIgnoreCase("videourl")) {
cmd.setVideoURL(tempVal);
}
...
public class SAXXMLParser {
public static List<Cmd> parse(InputStream is) {
List<Cmd> response = null;
try {
// create a XMLReader from SAXParser
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser()
.getXMLReader();
// create a SAXXMLHandler
SAXXMLHandler saxHandler = new SAXXMLHandler();
// store handler in XMLReader
xmlReader.setContentHandler(saxHandler);
// the process starts
xmlReader.parse(new InputSource(is));
// get the `Video list`
response = saxHandler.getResponse();
} catch (Exception ex) {
Log.d("XML", "SAXXMLParser: parse() failed");
ex.printStackTrace();
}
// return Laptop list
return response;
}
}
...
public class Cmd {
private String success;
private String cmd;
private String videos;
private String video;
private String numberofvideos;
private String videoname;
private String videourl;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
public String getNumberOfVideos() {
return numberofvideos;
}
public void setNumberOfVideos(String numberofvideos) {
this.numberofvideos = numberofvideos;
}
public String getVideos() {
return videos;
}
public void setVideos(String videos) {
this.videos = videos;
}
public String getVideo() {
return video;
}
public void setVideo(String video) {
this.video = video;
}
public String getVideoName() {
return videoname;
}
public void setVideoName(String videoname) {
this.videoname = videoname;
}
public String getVideoURL() {
return videourl;
}
public void setVideoURL(String videourl) {
this.video = videourl;
}
Please let me know if any additional information is required. I will be happy to provide it.
Your code sets the member video instead of videourl in setvideourl method
public void setVideoURL(String videourl) {
this.video = videourl;<<----
This.videourl = videourl;
}
I am using simple-xml to perform XML serialization/deserialization in my Java application. I have a class as follows:
#Root(name="config")
public class Config{
#Element(name="update_interval")
private int updateInterval;
#Element(name="timestamp")
private long timestamp;
//...
//...
}
Now, this would produce XML like the following:
<config>
<update_interval>2000</update_interval>
<timestamp>1234567890</timestamp>
</config>
Question:
How can I override the element name at runtime, so that in some cases, the XML reads as follows?
<config>
<updt_int>2000</updt_int>
<ts>1234567890</ts>
</config>
Edit:
To clarify, I want to override the element names only in some cases. So basically,
if(condition){
//Override Element Names
} else {
//Serialize Normally
}
I found an easy way to achieve serialization in this case, thanks to this comment.
However, I haven't been able to de-serialize such an XML document. Here's my partial solution:
/*
* Config.java
*/
#Root(name="config", strict = false)
public class Config {
#Element(name="timestamp", required = false)
private long timestamp;
#Element(name = "update_interval", required = false)
private int updateInterval;
public Config() {
}
public int getUpdateInterval() {
return updateInterval;
}
public void setUpdateInterval(int updateInterval) {
this.updateInterval = updateInterval;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
#Override
public String toString() {
return "Config{" +
"timestamp=" + timestamp +
", updateInterval=" + updateInterval +
'}';
}
}
/*
* Custom Visitor implementation
*/
public class MyInterceptor implements Visitor {
private static int sReadCount = 0;
private static int sWriteCount = 0;
#Override
public void read(Type field, NodeMap<InputNode> node) throws Exception {
/*
* This is where I need help!
*
*
* This method is only called once: for the <config> node
* It is not called for the other nodes since they are not "recognized"
* i.e., there are no annotations for the nodes <ts> and <updt_int>
*/
System.out.println("Read Count : "+ (++sReadCount));
System.out.println(node.getName());
System.out.println(node.getNode());
}
#Override
public void write(Type field, NodeMap<OutputNode> node) throws Exception {
/*
* This works like a charm.
*/
System.out.println("Write Count : "+ (++sWriteCount));
OutputNode opNode = node.getNode();
if("timestamp".equals(opNode.getName())){
opNode.setName("ts");
}
if("update_interval".equals(opNode.getName())){
opNode.setName("updt_int");
}
}
}
/*
*
*/ Main class
public class Bootstrap {
static final Random RANDOM = new Random();
public static void main(String [] args){
Config cfg = new Config();
cfg.setTimestamp(RANDOM.nextLong());
cfg.setUpdateInterval(1000);
Serializer serializer = new Persister(new VisitorStrategy(new MyInterceptor()));
StringWriter writer = new StringWriter();
try {
serializer.write(cfg, writer);
} catch (Exception e) {
e.printStackTrace();
}
String serialized = writer.toString();
System.out.println(serialized);
Config desCfg = null;
try {
desCfg = serializer.read(Config.class, serialized);
} catch (Exception e) {
e.printStackTrace();
}
if(desCfg != null){
System.out.println(desCfg.toString());
}
}
}