Background: I am trying to parse XML into object using JAXB unmarshaller.
What I've done: I used JAXB itself to generate object classes and wrote some methods to unmarshal xml.
public void xmlParser() {
try {
Acquirer acquirer = (Acquirer) readXml(Constants.XML_PATH);
System.out.println(acquirer.getDate());
} catch (JAXBException | IOException e) {
e.printStackTrace();
}
}
/**
* Initializes of JAXB Context and Unmarshaller.
*/
private static void createContext() {
try {
jaxbContext = JAXBContext.newInstance("com.test.xml.generated");
unmarshaller = jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
e.printStackTrace();
}
}
/**
* This reads the XML file from the resources in ClassPath.
*
* #param xmlFile XML file name as String with relative ClassPath
* #return Unmarashalled XML file
* #throws JAXBException
* #throws IOException
* #throws Exception
*/
public Object readXml(String xmlFile) throws JAXBException, IOException {
if (jaxbContext == null) {
createContext();
}
InputStream stream = getClass().getClassLoader().getResourceAsStream(xmlFile);
BufferedInputStream buffredStream = new BufferedInputStream(stream);
***Error:***
Object obj = unmarshaller.unmarshal(buffredStream);
buffredStream.close();
stream.close();
return obj;
}
Error is in Object obj.....
Exception:
javax.xml.bind.UnmarshalException - with linked exception:
[java.io.IOException: Stream closed]
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:246)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:125)
What I've managed to search: I used xml validator to validate xml and it seems fine. I also saw that someone suggested not to use InputStream and etc.. So I tried using File file = new File(); nothing. Moreover I tried to check auto generated object classes, but didn't find anything suspicious. #XmlElement and Root seems to be defined just fine.
P.S. I have xsd scheme of this xml (I generated all object classes using this xsd). I even used online tools to validate them both and everything seems right.
Constants.XML_PATH = "/Acquirer.xml";
Simply change:
InputStream stream = getClass().getClassLoader().getResourceAsStream(xmlFile);
on:
InputStream stream = getClass().getResourceAsStream(xmlFile);
Because when you use getClass().getClassLoader().getResourceAsStream(xmlFile) then it returns null (does not find the resource) and BufferedInputStream then throws the IOException when providing null instead of input stream instance into constructor.
Related
After doing a GIT rebase, out of nowhere, I started receiving this exception:
by: org.xml.sax.SAXParseException: "The content of elements must consist of well-formed character data or markup"
The Java code simply tries to unmarshall an xml file to a jaxb object. Something like this:
...
FGIXmlValidationEventCollector collector = new FGIXmlValidationEventCollector();
Unmarshaller unmarshaller = null;
try {
unmarshaller = context.createUnmarshaller();
unmarshaller.setSchema(getSchema());
unmarshaller.setEventHandler(collector);
} catch (JAXBException e) {
throw new FGIXmlException(e);
} catch (SAXException e) {
throw new FGIXmlException(e);
}
public Schema getSchema() throws SAXException {
SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
URL schemaLocation = getClass().getResource("/config/xsd/fgi.xsd");
**return schemaFactory.newSchema(schemaLocation); // exception is thrown here!!!**
}
I'm clueless. There was no code change that justifies this. This code has worked for many years. I don't know why after some trivial git pull/rebase I ended up with this behavior. By the way, there's no apparent problem whatsoever with the XML file that I want to unmarshall. It's an UFT-8 xml file. I've even tried changing the encoding but to no avail.
Any ideas?
Thank you
I need to figure out how to validate my XML files with schema's offline. After looking around for a couple of days, what I was able to find was basically that I needed to have an internal reference to the schema. I needed to find them, download them, and change the reference to a local system path. What I was unable to find was exactly how to do that. Where and how can I change the reference to point internally instead of externally? What is the best way to download the schemas?
There are three ways you could do this. What they all have in common is that you need a local copy of the schema document(s). I'm assuming that the instance documents currently use xsi:schemaLocation and/or xsi:noNamespaceSchemaLocation to point to a location holding the schema document(s) on the web.
(a) Modify your instance documents to refer to the local copy of the schema documents. This is usually inconvenient.
(b) Redirect the references so that a request for a remote file is redirected to a local file. The way to set this up depends on which schema validator you are using and how you are invoking it.
(c) Tell the schema processor to ignore the values of xsi:schemaLocation and xsi:noNamespaceSchemaLocation, and to validate instead against a schema that you supply using your schema processor's invocation API. Again the details depend on which schema processor you are using.
My preferred approach is (c): if only because when you are validating a source document, then by definition you don't fully trust it - so why should you trust it to contain a correct xsi:schemaLocation attribute?
XmlValidate is a simple but powerful command-line tool that can perform offline validation of single or multiple XML files against target schemas. It can scan local xml files by file name, directory, or URL.
XmlValidate automatically adds the schemaLocation based on the schema namespace and a config file that mapping to a local file. The tool will validate against whatever XML Schema is referenced in the config file.
Here are example mappings of namespace to target Schema in config file:
http://www.opengis.net/kml/2.2=${XV_HOME}/schemas/kml22.xsd
http://appengine.google.com/ns/1.0=C:/xml/appengine-web.xsd
urn:oasis:names:tc:ciq:xsdschema:xAL:2.0=C:/xml/xAL.xsd
Note that ${XV_HOME} token above is simply an alias for the top-level directory that XmlValidate is running from. The location can likewise be a full file path.
XmlValidate is an open-source project (source code available) that runs with the Java Runtime Environment (JRE). The bundled application (Java jars, examples, etc.) can be downloaded here.
If XmlValidate is run in batch mode against multiple XML files, it will provide a summary of validation results.
Errors: 17 Warnings: 0 Files: 11 Time: 1506 ms
Valid files 8/11 (73%)
You can set your own Implementation of ResourceResolver and LSInput to the SchemaFactory so that the call of
of LSInput.getCharacterStream() will provide a schema from a local path.
I have written an extra class to do offline validation. You can call it like
new XmlSchemaValidator().validate(xmlStream, schemaStream, "https://schema.datacite.org/meta/kernel-4.1/",
"schemas/datacite/kernel-4.1/");
Two InputStream are beeing passed. One for the xml, one for the schema. A baseUrl and a localPath (relative on classpath) are passed as third and fourth parameter. The last two parameters are used by the validator to lookup additional schemas locally at localPath or relative to the provided baseUrl.
I have tested with a set of schemas and examples from https://schema.datacite.org/meta/kernel-4.1/ .
Complete Example:
#Test
public void validate4() throws Exception {
InputStream xmlStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(
"schemas/datacite/kernel-4.1/example/datacite-example-complicated-v4.1.xml");
InputStream schemaStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("schemas/datacite/kernel-4.1/metadata.xsd");
new XmlSchemaValidator().validate(xmlStream, schemaStream, "https://schema.datacite.org/meta/kernel-4.1/",
"schemas/datacite/kernel-4.1/");
}
The XmlSchemaValidator will validate the xml against the schema and will search locally for included Schemas. It uses a ResourceResolver to override the standard behaviour and to search locally.
public class XmlSchemaValidator {
/**
* #param xmlStream
* xml data as a stream
* #param schemaStream
* schema as a stream
* #param baseUri
* to search for relative pathes on the web
* #param localPath
* to search for schemas on a local directory
* #throws SAXException
* if validation fails
* #throws IOException
* not further specified
*/
public void validate(InputStream xmlStream, InputStream schemaStream, String baseUri, String localPath)
throws SAXException, IOException {
Source xmlFile = new StreamSource(xmlStream);
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) -> {
LSInput input = new DOMInputImpl();
input.setPublicId(publicId);
input.setSystemId(systemId);
input.setBaseURI(baseUri);
input.setCharacterStream(new InputStreamReader(
getSchemaAsStream(input.getSystemId(), input.getBaseURI(), localPath)));
return input;
});
Schema schema = factory.newSchema(new StreamSource(schemaStream));
javax.xml.validation.Validator validator = schema.newValidator();
validator.validate(xmlFile);
}
private InputStream getSchemaAsStream(String systemId, String baseUri, String localPath) {
InputStream in = getSchemaFromClasspath(systemId, localPath);
// You could just return in; , if you are sure that everything is on
// your machine. Here I call getSchemaFromWeb as last resort.
return in == null ? getSchemaFromWeb(baseUri, systemId) : in;
}
private InputStream getSchemaFromClasspath(String systemId, String localPath) {
System.out.println("Try to get stuff from localdir: " + localPath + systemId);
return Thread.currentThread().getContextClassLoader().getResourceAsStream(localPath + systemId);
}
/*
* You can leave out the webstuff if you are sure that everything is
* available on your machine
*/
private InputStream getSchemaFromWeb(String baseUri, String systemId) {
try {
URI uri = new URI(systemId);
if (uri.isAbsolute()) {
System.out.println("Get stuff from web: " + systemId);
return urlToInputStream(uri.toURL(), "text/xml");
}
System.out.println("Get stuff from web: Host: " + baseUri + " Path: " + systemId);
return getSchemaRelativeToBaseUri(baseUri, systemId);
} catch (Exception e) {
// maybe the systemId is not a valid URI or
// the web has nothing to offer under this address
}
return null;
}
private InputStream urlToInputStream(URL url, String accept) {
HttpURLConnection con = null;
InputStream inputStream = null;
try {
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(15000);
con.setRequestProperty("User-Agent", "Name of my application.");
con.setReadTimeout(15000);
con.setRequestProperty("Accept", accept);
con.connect();
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307
|| responseCode == 303) {
String redirectUrl = con.getHeaderField("Location");
try {
URL newUrl = new URL(redirectUrl);
return urlToInputStream(newUrl, accept);
} catch (MalformedURLException e) {
URL newUrl = new URL(url.getProtocol() + "://" + url.getHost() + redirectUrl);
return urlToInputStream(newUrl, accept);
}
}
inputStream = con.getInputStream();
return inputStream;
} catch (SocketTimeoutException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private InputStream getSchemaRelativeToBaseUri(String baseUri, String systemId) {
try {
URL url = new URL(baseUri + systemId);
return urlToInputStream(url, "text/xml");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
prints
Try to get stuff from localdir: schemas/datacite/kernel-4.1/http://www.w3.org/2009/01/xml.xsd
Get stuff from web: http://www.w3.org/2009/01/xml.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-titleType-v4.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-contributorType-v4.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-dateType-v4.1.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-resourceType-v4.1.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-relationType-v4.1.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-relatedIdentifierType-v4.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-funderIdentifierType-v4.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-descriptionType-v4.xsd
Try to get stuff from localdir: schemas/datacite/kernel-4.1/include/datacite-nameType-v4.1.xsd
The print shows that the validator was able to validate against a set of local schemas. Only http://www.w3.org/2009/01/xml.xsd was not available locally and therefore fetched from the internet.
I'm new to Java and I'm porting one of my C++ libraries to Java as a learning experiment. This is not homework (as should be obvious from looking at my code). I have a few questions concerning the following code of my constructor for an ESRI shape file reader.
import java.io.*;
/**
*
* #author Bill
*/
public class ShapeFileReader {
public FileInputStream inStream;
/*
* #param fileName File name string. Must not be null or zero length.
* #throws Exception if file specified by fileName fails to open
*/
public ShapeFileReader(String fileName) throws IOException {
if(fileName == null)
throw new NullPointerException("fileName was null");
if(fileName.length() == 0)
throw new IllegalArgumentException("fileName string length was zero");
File fi = new File(fileName);
if(fi.exists() == false)
throw new IOException("File does not exist: " + fileName);
// Catch-or-specify (this method already throws IOException)
inStream = new FileInputStream(fileName);
}
}
During parameter validation and existence should I be throwing the exceptions as shown? The validation throws unchecked exceptions, and the existence throws checked exceptions. I assume that FileInputStream constructor will also throw an IOException, but I specified that in the method throws clause.
I was considering refactoring the opening of the file to a seperate function, but I figured it would be more useful and simple to do this in the constructor, and also learns me how to control errors here. Besides, any instance of this object will not have a closed/open state. All of these objects are reserved strictly for READING a file only, so they are created on a as-needed basis per file. I will provide a close() method seperately.
Also, from an extensibility point of view, would it be difficult to adapt to reading a file over a network using the FileInputStream with the current constructor? Or should I use a different class and multiple constructors?
Thanks for any and all input.
I wouldn't bother with the exceptions, FileInputStream will throw an exception for you, you're not adding anything other than clutter to your code.
For it to work with the network rather than just a file you'd modify thus:
public class ShapeFileReader {
private final InputStream inStream;
public ShapeFileReader(InputStream inStream) {
this.inStream = inStream;
}
/*
* #param fileName File name string. Must not be null or zero length.
* #throws Exception if file specified by fileName fails to open
*/
public ShapeFileReader(String fileName) throws IOException {
this(new FileInputStream(fileName));
}
Since this has been accepted as the answer I'm editing it as Roland (in the comments) is quite correct and this isn't how I'd have approached the problem.
public class ShapeReader {
public static Shape readShape(InputStream inStream) {
... do the work
}
/*
* #param fileName File name string. Must not be null or zero length.
* #throws Exception if file specified by fileName fails to open
*/
public static Shape readShape(String fileName) throws IOException {
FileInputStream fis = new FileInputStream(fileName);
try {
return readShape(fis);
} finally {
fis.close();
}
}
}
Hy i need to parse this XML :
<WhoisRecord xmlns="http://adam.kahtava.com/services/whois" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<DomainName>68.140.1.1</DomainName>
<RegistryData><AbuseContact><Email>abuse-mail#verizonbusiness.com</Email><Name>abuse</Name><Phone>+1-800-900-0241</Phone></AbuseContact><AdministrativeContact><Email>stephen.r.middleton#verizon.com</Email><Name>Verizon Internet Services</Name><Phone>800-243-6994</Phone></AdministrativeContact><BillingContact i:nil="true"/><CreatedDate>2002-05-13T00:00:00-04:00</CreatedDate><RawText i:nil="true"/><Registrant><Address>22001 Loudoun County Parkway</Address><City>Ashburn</City><Country>US</Country><Name>UUNET Technologies, Inc.</Name><PostalCode>20147</PostalCode><StateProv>VA</StateProv></Registrant><TechnicalContact><Email>swipper#verizonbusiness.com</Email><Name>swipper</Name><Phone>+1-800-900-0241</Phone></TechnicalContact><UpdatedDate>2004-03-16T00:00:00-05:00</UpdatedDate><ZoneContact i:nil="true"/></RegistryData></WhoisRecord>
My code looks like this:
public class XMLParser
{
String streamTitle = "";
/** Called when the activity is first created.
* #throws IOException
* #throws SAXException */
public String startparse(String xml) throws SAXException, IOException
{
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
try
{
DocumentBuilder builder = builderFactory.newDocumentBuilder();
builder.parse(xml);
}
catch (ParserConfigurationException e)
{
e.printStackTrace();
}
return builderFactory.getAttribute("WhoisRecord").toString();
}
}
when i try to return something from startparse i simple get nothing.
XMLParser xmlpar = new XMLParser();
Log.v("Faruk TEST ", "udss:"+xmlpar.startparse(temp));
Do some one know a simple solution for this problem?
Try this i hope it is what you are looking for ...
Android: parse XML from string problems
I think that your call to builderFactory.getAttribute is wrong.
DocumentBuilder.parse() returns a Document object, and this will contain the DOM that you've just parsed. You can use this to access the elements of the XML.
Try this Working with XML-Android
At work, we have just migrated an old web-app from struts 1.1 to 1.2.9 (hopefully first steps to moving to 1.3), but we are now having problems with the commons digester. Struts 1.2.9 uses commons-digester 1.6.
When we try to parse one of our XML files we get the exception:
org.xml.sax.SAXParseException: Attribute "" bound to namespace "null" was already specified for element "metric".
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:232)
at org.apache.xerces.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:213)
at org.apache.xerces.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:385)
at org.apache.xerces.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:315)
at org.apache.xerces.impl.dtd.XMLNSDTDValidator.startNamespaceScope(XMLNSDTDValidator.java:242)
at org.apache.xerces.impl.dtd.XMLDTDValidator.handleStartElement(XMLDTDValidator.java:1980)
at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement(XMLDTDValidator.java:802)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:313)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(XMLNSDocumentScannerImpl.java:610)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(XMLDocumentFragmentScannerImpl.java:1608)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:346)
at org.apache.xerces.parsers.DTDConfiguration.parse(DTDConfiguration.java:529)
at org.apache.xerces.parsers.DTDConfiguration.parse(DTDConfiguration.java:585)
at org.apache.xerces.parsers.XMLParser.parse(XMLParser.java:152)
at org.apache.xerces.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1142)
at org.apache.commons.digester.Digester.parse(Digester.java:1572)
at com.foo.ctms.framework.metrics.parser.MetricsXMLParser$InternalDigester.parse(MetricsXMLParser.java:54)
at com.foo.ctms.framework.metrics.parser.MetricsXMLParser.parse(MetricsXMLParser.java:40)
In investigating this issue I have tried to get a simplest possible case and this is what I currently have:
package com.foo.ctms.framework.metrics.parser;
import org.apache.commons.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.net.URL;
/**
* Class that provides methods for parsing metrics definitions defined via XML and populating an object graph of JavaBeans
* representing the definition.
* #version $Revision: 41470 $
*/
public final class MetricsXMLParser {
/**
* The set of public identifiers, and corresponding resource names, for the versions of the configuration file DTD that we know
* about. The key is the name of the resource as in the XMl file, and the value is the location of the resource with respect to
* the <code>ClassLoader</code> that this code in running in.
*/
private static final String registrations[] = {"-//Foo Inc.//DTD Portal Metrics 1.0//EN", "metrics.dtd"};
private MetricsXMLParser() {
}
/**
* Parses a metric definition specified as an <code>InputStream</code>.
* #param url The metrics definition to parse. Must not be <code>null</code>.
* #throws IOException if an I/O error occured while attempting to parse
* #throws SAXException if an XML parsing error occured
*/
public static MetricDefinition parse(URL url)
throws IOException, SAXException {
InternalDigester digester = new InternalDigester();
return digester.parse(url);
}
private static final class InternalDigester {
private final Digester digester;
/**
* Parses a metric definition specified as an <code>InputStream</code>.
* #param input The metrics definition to parse. Must not be <code>null</code>.
* #throws IOException if an I/O error occured while attempting to parse
* #throws SAXException if an XML parsing error occured
*/
public MetricDefinition parse(URL input)
throws IOException, SAXException {
return (MetricDefinition)digester.parse(new InputSource(input.toString()));
}
private InternalDigester() {
digester = new Digester();
digester.setValidating(true);
for (int i = 0; i < MetricsXMLParser.registrations.length; i += 2) {
URL url = getClass().getResource(MetricsXMLParser.registrations[i + 1]);
if (url != null) {
digester.register(MetricsXMLParser.registrations[i], url.toString());
}
}
digester.addObjectCreate("metric", MetricDefinition.class);
digester.addSetProperties("metric");
}
}
}
which is given the XML:
<?xml version='1.0' encoding='windows-1252'?>
<!DOCTYPE metric PUBLIC "-//Foo Inc.//DTD Portal Metrics 1.0//EN" "metrics.dtd">
<metric name="metricsConfig" defaultView="trials">
</metric>
The DTD is currently down to:
<!-- A metric element is the document root -->
<!ELEMENT metric ANY>
<!-- A metric has a name and a default view. The default view must
exactly match the name of one of the nested views -->
<!ATTLIST metric
name CDATA #REQUIRED
defaultView CDATA #IMPLIED
>
Does anyone know what I am doing wrong?
If I remove the defaultView attribute I do not get the error.
As per sfussenegger's suggestion I've now tried the following (non-digester) code:
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(true);
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.parse(new InputSource(url.toString()));
} catch (Exception e) {
e.printStackTrace();
}
and couldn't reproduce the problem, but adding the following (which commons-digester also does in XercesParser) prior to using my factory gives the same exception:
factory.setFeature("http://apache.org/xml/features/validation/dynamic", true);
factory.setFeature("http://apache.org/xml/features/validation/schema", true);
In the end we have decided to try out a more modern version of xerces (2.7.1) and that seems to work.
Problem appears to have been caused by an incompatibility between commons-digester 1.6 and xerces (call in XercesParser says 2.2), moving to a more recent version of xerces (2.7.1) has fixed the problem.
We chose this version of the library because other web applications we have are already using it.