JAXB alternative on Android for XML Schema? - android

Is there any lightweight library for Android that acts like JAXB on the desktop?
Give an XML schema, create code to parse, validate, manipulate and then again write it.
The files already exist and since it's a finance application nothing that isn't modified must be touched. Including whitespace, ordering and character encoding.
(I'm doing this for years with JAXB and it works fine but I can't port that code to Android due to the lack of JAXB and it's footprint.)

You can try Simple Framework or Castor.

The following worked for me for validation:
Create a validation utility.
Get both the xml and xsd into file on the android OS and use the validation utility against it.
Use Xerces-For-Android to do the validation.
Android does support some packages which we can use, I created my xml validation utility based on: http://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/validation/package-summary.html
My initial sandbox testing was pretty smooth with java, then I tried to port it over to Dalvik and found that my code did not work. Some things just aren't supported the same with Dalvik, so I made some modifications.
I found a reference to xerces for android, so I modified my sandbox test of (the following doesn't work with android, the example after this does):
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
/**
* A Utility to help with xml communication validation.
*/
public class XmlUtil {
/**
* Validation method.
* Base code/example from: http://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/validation/package-summary.html
*
* #param xmlFilePath The xml file we are trying to validate.
* #param xmlSchemaFilePath The schema file we are using for the validation. This method assumes the schema file is valid.
* #return True if valid, false if not valid or bad parse.
*/
public static boolean validate(String xmlFilePath, String xmlSchemaFilePath) {
// parse an XML document into a DOM tree
DocumentBuilder parser = null;
Document document;
// Try the validation, we assume that if there are any issues with the validation
// process that the input is invalid.
try {
// validate the DOM tree
parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
document = parser.parse(new File(xmlFilePath));
// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File(xmlSchemaFilePath));
Schema schema = factory.newSchema(schemaFile);
// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
} catch (Exception e) {
// Catches: SAXException, ParserConfigurationException, and IOException.
return false;
}
return true;
}
}
The above code had to be modified some to work with xerces for android (http://gc.codehum.com/p/xerces-for-android/). You need SVN to get the project, the following are some crib notes:
download xerces-for-android
download silk svn (for windows users) from http://www.sliksvn.com/en/download
install silk svn (I did complete install)
Once the install is complete, you should have svn in your system path.
Test by typing "svn" from the command line.
I went to my desktop then downloaded the xerces project by:
svn checkout http://xerces-for-android.googlecode.com/svn/trunk/ xerces-for-android-read-only
You should then have a new folder on your desktop called xerces-for-android-read-only
With the above jar (Eventually I'll make it into a jar, just copied it directly into my source for quick testing. If you wish to do the same, you can making the jar quickly with Ant (http://ant.apache.org/manual/using.html)), I was able to get the following to work for my xml validation:
import java.io.File;
import java.io.IOException;
import mf.javax.xml.transform.Source;
import mf.javax.xml.transform.stream.StreamSource;
import mf.javax.xml.validation.Schema;
import mf.javax.xml.validation.SchemaFactory;
import mf.javax.xml.validation.Validator;
import mf.org.apache.xerces.jaxp.validation.XMLSchemaFactory;
import org.xml.sax.SAXException;
/**
* A Utility to help with xml communication validation.
*/public class XmlUtil {
/**
* Validation method.
*
* #param xmlFilePath The xml file we are trying to validate.
* #param xmlSchemaFilePath The schema file we are using for the validation. This method assumes the schema file is valid.
* #return True if valid, false if not valid or bad parse or exception/error during parse.
*/
public static boolean validate(String xmlFilePath, String xmlSchemaFilePath) {
// Try the validation, we assume that if there are any issues with the validation
// process that the input is invalid.
try {
SchemaFactory factory = new XMLSchemaFactory();
Source schemaFile = new StreamSource(new File(xmlSchemaFilePath));
Source xmlSource = new StreamSource(new File(xmlFilePath));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(xmlSource);
} catch (SAXException e) {
return false;
} catch (IOException e) {
return false;
} catch (Exception e) {
// Catches everything beyond: SAXException, and IOException.
e.printStackTrace();
return false;
} catch (Error e) {
// Needed this for debugging when I was having issues with my 1st set of code.
e.printStackTrace();
return false;
}
return true;
}
}
Some Side Notes:
For creating the files, I made a simple file utility to write string to files:
public static void createFileFromString(String fileText, String fileName) {
try {
File file = new File(fileName);
BufferedWriter output = new BufferedWriter(new FileWriter(file));
output.write(fileText);
output.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
I also needed to write to an area that I had access to, so I made use of:
String path = this.getActivity().getPackageManager().getPackageInfo(getPackageName(), 0).applicationInfo.dataDir;
A little hackish, it works. I'm sure there is a more succinct way of doing this, however I figured I'd share my success, as there weren't any good examples that I found.

Related

Access WordNet dict files in Android app

I'm writing a word game in Android. It's my first app so my knowledge is almost non-existent.
What I would like to do is use JWI to access the WordNet dictionary. This requires specifying the WordNet dictionary's file path.
From what I've read, Android "assets" are not available via a simple file path, but what JWI requires to initialize the WordNet dictionary API is a URL to the disk location of the dictionary files.
So, what is the best course of action? Should I copy the assets at startup-time into a known folder on the android device? I can't think of a better way but that seems entirely stupid to me.
Any help gratefully received.
I have the same problem (for a jetty webapp however and not android) and tried those two approaches, however unsuccessfully:
JWNL.initialize(this.getClass().getClassLoader().getResourceAsStream("wordnet_properties.xml");
dict = Dictionary.getInstance();
Here it successfully loads wordnet_properties.xml but it cannot access the dictionary which is pointed to by the properties file.
Using the dictionary folder directly:
String dictPath = "models/en/wordnet/dict/";
URL url = this.getClass().getClassLoader().getResource(dictPath);
System.out.println("loading wordnet from "+url);
dict = new RAMDictionary(url, ILoadPolicy.NO_LOAD);
Here I get the dictionary URL to be jar:file:/home/myusername/.m2/repository/package/1.0-SNAPSHOT/commons-1.0-SNAPSHOT.jar!/models/en/wordnet/dict/. WordNet however doesn't accept the jar protocol and gives me the error:
java.lang.IllegalArgumentException: URL source must use 'file' protocol
at edu.mit.jwi.data.FileProvider.toFile(FileProvider.java:693)
at edu.mit.jwi.data.FileProvider.open(FileProvider.java:304)
at edu.mit.jwi.DataSourceDictionary.open(DataSourceDictionary.java:92)
at edu.mit.jwi.RAMDictionary.open(RAMDictionary.java:216)
My next investigation will be to create a subclass to RAMDictionary or something similar, please tell me if you have found a solution in the meantime.
P.S.: I just wrote the developer a mail asking for help after I tried to rewrite the FileProvider to use resources instead but after one or two hours I gave up because the code calls so much other code that also only works with files. I will keep you up to date!
P.P.S.: I received an answer from the developer saying that it is principially not possible with streams because they don't offer random access which is necessary. However, he offered to implement a solution to load it all in RAM, if really necessary, but that would use up about 500 MB and I guess that is too much for android apps so I guess it is still best to unpack it somewhere.
P.S.: Here is my unpacking solution (you can replace the System.out.println statements with logger statements if you use logging or remove them if you don't like them):
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/** Allows WordNet to be run from within a jar file by unpacking it to a temporary directory.**/
public class WordNetUnpacker
{
static final String ID = "178558556719"; // minimize the chance of interfering with an existing directory
static final String jarDir = "models/en/wordnet/dict";
/**If running from within a jar, unpack wordnet from the jar to a temp directory (if not already done) and return that.
* If not running from a jar, just return the existing wordnet directory.
* #see getUnpackedWordNetDir(Class)*/
static File getUnpackedWordNetDir() throws IOException
{return getUnpackedWordNetDir(WordNetUnpacker.class);}
/**If running from within a jar, unpack wordnet from the jar to a temp directory (if not already done) and return that.
* If not running from a jar, just return the existing wordnet directory.
* #param clazz the class in whose classloader the wordnet resources are found.
* #see getUnpackedWordNetDir()**/
static File getUnpackedWordNetDir(Class clazz) throws IOException
{
String codeSource = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
System.out.println("getUnpackedWordNetDir: using code source "+codeSource);
if(!codeSource.endsWith(".jar"))
{
System.out.println("not running from jar, no unpacking necessary");
try{return new File(WordNetUnpacker.class.getClassLoader().getResource(jarDir).toURI());}
catch (URISyntaxException e) {throw new IOException(e);}
}
try(JarFile jarFile = new JarFile(codeSource))
{
String tempDirString = System.getProperty("java.io.tmpdir");
if(tempDirString==null) {throw new IOException("java.io.tmpdir not set");}
File tempDir = new File(tempDirString);
if(!tempDir.exists()) {throw new IOException("temporary directory does not exist");}
if(!tempDir.isDirectory()) {throw new IOException("temporary directory is a file, not a directory ");}
File wordNetDir = new File(tempDirString+'/'+"wordnet"+ID);
wordNetDir.mkdir();
System.out.println("unpacking jarfile "+jarFile.getName());
copyResourcesToDirectory(jarFile, jarDir, wordNetDir.getAbsolutePath());
return wordNetDir;
}
}
/** Copies a directory from a jar file to an external directory. Copied from Stack Overflow. */
public static void copyResourcesToDirectory(JarFile fromJar, String jarDir, String destDir) throws IOException
{
int copyCount = 0;
for (Enumeration<JarEntry> entries = fromJar.entries(); entries.hasMoreElements();)
{
JarEntry entry = entries.nextElement();
if(!entry.getName().contains("models")) continue;
if (entry.getName().startsWith(jarDir) && !entry.isDirectory()) {
copyCount++;
File dest = new File(destDir + "/" + entry.getName().substring(jarDir.length() + 1));
File parent = dest.getParentFile();
if (parent != null) {
parent.mkdirs();
}
FileOutputStream out = new FileOutputStream(dest);
InputStream in = fromJar.getInputStream(entry);
try {
byte[] buffer = new byte[8 * 1024];
int s = 0;
while ((s = in.read(buffer)) > 0) {
out.write(buffer, 0, s);
}
} catch (IOException e) {
throw new IOException("Could not copy asset from jar file", e);
} finally {
try {
in.close();
} catch (IOException ignored) {}
try {
out.close();
} catch (IOException ignored) {}
}
}
}
if(copyCount==0) System.out.println("Warning: No files copied!");
}
}
You can just copy all dict files from "assets" to the internal directory of your app. Just do it once, on the first app launch.
Since then you can use JWI in a causual way like this:
String path = getFilesDir() + "/dict";
URL url = new URL("file", null, path);
IDictionary dict = new Dictionary(url);

Upload files from a folder to a FTP-Server. I've get two errors. How can I solve these errors?

I'm working on a FTP-Client.
I've already created an FTP-Client based by this one: Android How to upload a file via FTP
With this code. It was possible to send a txt-File with an included string to my FTP-Server.
I've got pictures in my local folder "/sdcard/ftp/"
Now I'm trying to change the code, that it is possible that all the jpg-files in the folder will be send by ftp client.
package de.android.datenuebertragung;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPFile;
import android.app.Activity;
import android.util.Log;
public class FTPManager extends Activity{
FTPClient con = new FTPClient();{
try
{
con.connect("host");
if (con.login("user", "password"))
{
// in application never use hardcoded paths
File folder = new File("/sdcard/ftp/");
File tempFile = null;
FTPFile[] tempFTPFile = null;
//get all files in folder
for(String fileInDir : folder.list()){
tempFile = new File(fileInDir);
tempFTPFile = con.listNames(tempFile.getFiles);
if(tempFTPFile!=null || tempFTPFile.length<=0){
con.upload(fileInDir);
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
try
{
con.logout();
con.disconnect();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
I've got two errors in the these marked words (getFiles and upload):
tempFTPFile = con.listNames(tempFile.getFiles);
con.upload(fileInDir);
The error in con.listNames(tempFile.getFiles) is: getFiles cannot be resolved or is not a field
and the another error in con.upload(fileInDir) is: The method upload(String) is undefined for the type FTPClient
I've tried the whole day to solve this problem. Hope someone can help me. Maybe post the changed and worked code. I'm an absolutley newbie in Android programming.
If you've pasted your code directly and there isn't a typo then it should be getFiles() and not getFiles in this line...
tempFTPFile = con.listNames(tempFile.getFiles);
Also, I suspect the default transfer mode will be ASCII (this is usually the case for any FTP client software).
Try calling...
con.setFileType(FTP.BINARY_FILE_TYPE);
...before attempting the uploads.

determine whether android asset entry is a file or directory

Hej,
I have some data shipped out with the app which shall be copied on the external storage. It's nested in a couple of subfolders and I'd like to copy the whole structure.
I'm having a hard time getting a File object for any ressource in /assets. But I think I'm depended on that 'cause I need something like File.isDirectory() to determine if I have to start copying or dive deeper into the system.
My first approach was using Assets Manager but it seems that class is not providing the information I need. The most promising why was to obtain an AssetFileDescriptorand go down to a [FileDescriptor][2]. However non of them seems to have a isDirectory-method.
So my other approach is straight forward: Creating a File Object and be happy. However it seems like I'm running in this problem of lacking a proper path to instance the file object. I'm aware of file://android_asset but it doesn't seem to work for the fileconstructor.
My last idea would to utilise the InputStream (which I need for copying anyway) and somehow filter the byte for a significant bit which indicates this resource to be a directory. That's a pretty hacky solution and probably right in the hell of ineffectiveness but I don't see another way to get around that.
I had the same problem. At some point I realized that list() is really slow (50ms on every call), so i'm using a different approach now:
I have an (eclipse) ant-builder which creates an index-file everytime my asset-folder changes. The file just contains one file-name per line, so directories are listed implicitely (if they are not empty).
The Builder:
<?xml version="1.0"?>
<project default="createAssetIndex">
<target name="createAssetIndex">
<fileset id="assets.fileset" dir="assets/" includes="**"
excludes="asset.index" />
<pathconvert pathsep="${line.separator}" property="assets"
refid="assets.fileset">
<mapper>
<globmapper from="${basedir}/assets/*" to="*"
handledirsep="yes" />
</mapper>
</pathconvert>
<echo file="assets/asset.index">${assets}</echo>
</target>
</project>
The class which loads asset.index into a List of Strings, so you can do arbitrary stuff with it, fast:
import android.content.ContextWrapper;
import com.google.common.collect.ImmutableList;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* uses asset.index file (which is pregenerated) since asset-list()s take very long
*
*/
public final class AssetIndex {
//~ Static fields/initializers -------------------------------------------------------------------------------------
private static final Logger L = LoggerFactory.getLogger(AssetIndex.class);
//~ Instance fields ------------------------------------------------------------------------------------------------
private final ImmutableList<String> files;
//~ Constructors ---------------------------------------------------------------------------------------------------
public AssetIndex(final ContextWrapper contextWrapper) {
ImmutableList.Builder<String> ib = ImmutableList.builder();
L.debug("creating index from assets");
InputStream in = null;
Scanner scanner = null;
try {
in = contextWrapper.getAssets().open("asset.index");
scanner = new Scanner(new BufferedInputStream(in));
while (scanner.hasNextLine()) {
ib.add(scanner.nextLine());
}
scanner.close();
in.close();
} catch (final IOException e) {
L.error(e.getMessage(), e);
} finally {
if (scanner != null) {
scanner.close();
}
if (in != null) {
try {
in.close();
} catch (final IOException e) {
L.error(e.getMessage(), e);
}
}
}
this.files = ib.build();
}
//~ Methods --------------------------------------------------------------------------------------------------------
/* returns the number of files in a directory */
public int numFiles(final String dir) {
String directory = dir;
if (directory.endsWith(File.separator)) {
directory = directory.substring(0, directory.length() - 1);
}
int num = 0;
for (final String file : this.files) {
if (file.startsWith(directory)) {
String rest = file.substring(directory.length());
if (rest.charAt(0) == File.separatorChar) {
if (rest.indexOf(File.separator, 1) == -1) {
num = num + 1;
}
}
}
}
return num;
}
}
list() on AssetManager will probably give a null / zero length array / IOException if you try to get a list on a file, but a valid response on a directory.
But otherwise it should be file:///android_asset (with 3 /)
In my specific case, regular files have a name like filename.ext, while directories only have a name, without extension, and their name never contains the "." (dot) character.
So a regular file can be distinguished from a directory by testing its name as follows:
filename.contains(".")
If this your case too, the same solution should work for you.

Android - verify the signature of XML

I have signed XML document (by pure Java with RSA and X509 tags) on the web and I have implemented XML pull parser - before I parse some information from my XML document to specific URL, I need to verify this document if it is the right one. Do you know how to check XML signature?
Thanks
edit:
my XML file looks like follows:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<application id="1">
<appversion>1.0</appversion>
<obligatory>yes</obligatory>
<update>http://www.xyz.....</update>
<check>http://www.xyz.....</check>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#1">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>fuv...</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>PC0+4uO4...</ds:SignatureValue>
<ds:KeyInfo>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>gEs/Fn2Gd5evwhlUgoS3...</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=abc abc,OU=abcs,O=abc,L=abc,ST=abc,C=abc</ds:X509IssuerName>
<ds:X509SerialNumber>123456...</ds:X509SerialNumber>
</ds:X509IssuerSerial>
<ds:X509SubjectName>CN=abc abc,OU=abcs,O=abc,L=abc,ST=abc,C=abc</ds:X509SubjectName>
<ds:X509Certificate>MIIDhzCCAm+gAwIBAgI...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
In J2EE Java you would use javax.xml.crypto as detailed here
http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/
However these are not part of the standard Android package.
It may be a manageable amount of work to make your own package of the bits of the source you need.
http://google.com/codesearch/p?hl=en#-WpwJU0UKqQ/src/share/classes/javax/xml/crypto/dom/DOMCryptoContext.java&d=5
You can use Apache Santuario but you need to strip it down. Look at https://web.archive.org/web/20140902223147/http://www.xinotes.net/notes/note/1302/ for more details.
You can add the following stripped version of Apache Santuario with the XML security features: http://mvnrepository.com/artifact/org.apache.santuario/xmlsec/2.0.2
Then you just need to create some verifier class, for example:
import java.io.*;
import javax.xml.parsers.*;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import org.w3c.dom.*;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
enter code here
public class Whatever {
boolean verifySignature() {
boolean valid = false;
try {
// parse the XML
InputStream in = obtainInputStreamToXMLSomehow();
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
Document doc = f.newDocumentBuilder().parse(in);
in.close();
// verify signature
NodeList nodes = doc.getElementsByTagNameNS(Constants.SignatureSpecNS, "Signature");
if (nodes.getLength() == 0) {
throw new Exception("Signature NOT found!");
}
Element sigElement = (Element) nodes.item(0);
XMLSignature signature = new XMLSignature(sigElement, "");
KeyInfo ki = signature.getKeyInfo();
if (ki == null) {
throw new Exception("Did not find KeyInfo");
}
X509Certificate cert = signature.getKeyInfo().getX509Certificate();
if (cert == null) {
PublicKey pk = signature.getKeyInfo().getPublicKey();
if (pk == null) {
throw new Exception("Did not find Certificate or Public Key");
}
valid = signature.checkSignatureValue(pk);
}
else {
valid = signature.checkSignatureValue(cert);
}
}
catch (Exception e) {
e.printStackTrace();
}
return valid;
}
// This is important!
static {
org.apache.xml.security.Init.init();
}
}
Edit
Seems like including XML parsing libraries in Android is trickier than expected. First you need to generate the jar file and then modify some of the library namespaces (using JarJar tool). After that, add it to the project's library directory (/lib or /libs). Source
I switched from signed XML to Signed JSON (RFC 7515)
Maybe you can use Apache Santuario. http://santuario.apache.org/

SchemaFactory doesn't support W3C XML Schema in platform level 8?

With the Android SDK, the following code in a plain empty Activity fails:
#Override
protected void onStart() {
super.onStart();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
}
The 2.2 emulator logcat shows this exception:
06-28 05:38:06.107: WARN/dalvikvm(495): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
06-28 05:38:06.128: ERROR/AndroidRuntime(495): FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.HelloWorldActivity}: java.lang.IllegalArgumentException: http://www.w3.org/2001/XMLSchema
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
at android.app.ActivityThread.access$2300(ActivityThread.java:125)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: http://www.w3.org/2001/XMLSchema
at javax.xml.validation.SchemaFactory.newInstance(SchemaFactory.java:194)
at com.example.HelloWorldActivity.onStart(HelloWorldActivity.java:26)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129)
at android.app.Activity.performStart(Activity.java:3781)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2636)
... 11 more
The Javadoc of SchemaFactory mentions "Platform default SchemaFactory is located in a implementation specific way. There must be a platform default SchemaFactory for W3C XML Schema."
I had the same problem and read many posts before I got an answer that worked for me. The reference to the constant won't work on Dalvik. I found I had to modify my code to work with the Xerces-for-Android project then I was able to get the xml validation I was after. Which is most likely what you are doing by the variable reference. The following are details of the setup and some example code showing how to get the validation to work on android.
The following worked for me:
Create a validation utility.
Get both the xml and xsd into file on the android OS and use the validation utility against it.
Use Xerces-For-Android to do the validation.
Android does support some packages which we can use, I created my xml validation utility based on: http://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/validation/package-summary.html
My initial sandbox testing was pretty smooth with java, then I tried to port it over to Dalvik and found that my code did not work. Some things just aren't supported the same with Dalvik, so I made some modifications.
I found a reference to xerces for android, so I modified my sandbox test of (the following doesn't work with android, the example after this does):
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
/**
* A Utility to help with xml communication validation.
*/
public class XmlUtil {
/**
* Validation method.
* Base code/example from: http://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/validation/package-summary.html
*
* #param xmlFilePath The xml file we are trying to validate.
* #param xmlSchemaFilePath The schema file we are using for the validation. This method assumes the schema file is valid.
* #return True if valid, false if not valid or bad parse.
*/
public static boolean validate(String xmlFilePath, String xmlSchemaFilePath) {
// parse an XML document into a DOM tree
DocumentBuilder parser = null;
Document document;
// Try the validation, we assume that if there are any issues with the validation
// process that the input is invalid.
try {
// validate the DOM tree
parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
document = parser.parse(new File(xmlFilePath));
// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File(xmlSchemaFilePath));
Schema schema = factory.newSchema(schemaFile);
// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
} catch (Exception e) {
// Catches: SAXException, ParserConfigurationException, and IOException.
return false;
}
return true;
}
}
The above code had to be modified some to work with xerces for android (http://gc.codehum.com/p/xerces-for-android/). You need SVN to get the project, the following are some crib notes:
download xerces-for-android
download silk svn (for windows users) from http://www.sliksvn.com/en/download
install silk svn (I did complete install)
Once the install is complete, you should have svn in your system path.
Test by typing "svn" from the command line.
I went to my desktop then downloaded the xerces project by:
svn checkout http://xerces-for-android.googlecode.com/svn/trunk/ xerces-for-android-read-only
You should then have a new folder on your desktop called xerces-for-android-read-only
With the above jar (Eventually I'll make it into a jar, just copied it directly into my source for quick testing. If you wish to do the same, you can making the jar quickly with Ant (http://ant.apache.org/manual/using.html)), I was able to get the following to work for my xml validation:
import java.io.File;
import java.io.IOException;
import mf.javax.xml.transform.Source;
import mf.javax.xml.transform.stream.StreamSource;
import mf.javax.xml.validation.Schema;
import mf.javax.xml.validation.SchemaFactory;
import mf.javax.xml.validation.Validator;
import mf.org.apache.xerces.jaxp.validation.XMLSchemaFactory;
import org.xml.sax.SAXException;
/**
* A Utility to help with xml communication validation.
*/public class XmlUtil {
/**
* Validation method.
*
* #param xmlFilePath The xml file we are trying to validate.
* #param xmlSchemaFilePath The schema file we are using for the validation. This method assumes the schema file is valid.
* #return True if valid, false if not valid or bad parse or exception/error during parse.
*/
public static boolean validate(String xmlFilePath, String xmlSchemaFilePath) {
// Try the validation, we assume that if there are any issues with the validation
// process that the input is invalid.
try {
SchemaFactory factory = new XMLSchemaFactory();
Source schemaFile = new StreamSource(new File(xmlSchemaFilePath));
Source xmlSource = new StreamSource(new File(xmlFilePath));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(xmlSource);
} catch (SAXException e) {
return false;
} catch (IOException e) {
return false;
} catch (Exception e) {
// Catches everything beyond: SAXException, and IOException.
e.printStackTrace();
return false;
} catch (Error e) {
// Needed this for debugging when I was having issues with my 1st set of code.
e.printStackTrace();
return false;
}
return true;
}
}
Some Side Notes:
For creating the files, I made a simple file utility to write string to files:
public static void createFileFromString(String fileText, String fileName) {
try {
File file = new File(fileName);
BufferedWriter output = new BufferedWriter(new FileWriter(file));
output.write(fileText);
output.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
I also needed to write to an area that I had access to, so I made use of:
String path = this.getActivity().getPackageManager().getPackageInfo(getPackageName(), 0).applicationInfo.dataDir;
A little hackish, it works. I'm sure there is a more succinct way of doing this, however I figured I'd share my success, as there weren't any good examples that I found.
You might have some luck re-packaging xerces with jarjar and then passing
"org.apache.xerces.jaxp.validation.XMLSchemaFactory"
to
SchemaFactory.newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader)
if you're using API >=9 or directly instantiating
org.apache.xerces.jaxp.validation.XMLSchemaFactory
if you're using API 8. It might not work at all using an older API than that.

Categories

Resources