I'm making a java application that checks if a XML file is already Canonical or not using XOM.
In my tests I have the following file which is already Canonical.
<doc xmlns="http://example.com/default" xmlns:x="http://example.com/x">
<a a1="1" a2="2">123</a>
<b xmlns:y="http://example.com/y" a3=""3"" y:a1="1" y:a2="2"></b>
</doc>
Here it is the code when I load it again with XOM.
<?xml version="1.0"?>
<doc xmlns="http://example.com/default" xmlns:x="http://example.com/x">
<a a1="1" a2="2">123</a>
<b xmlns:y="http://example.com/y" a3=""3"" y:a1="1" y:a2="2" />
</doc>
As you can see it adds again xml tag and delete the closing tag </b> because the value of tag b is empty.
I haven't got any problem with xml version tag but I don't know what to do to keep the closing tag </b> when I load the canonical document from file.
It looks like you are outputting the document with a XOM Serializer you need to use a XOM Canonicalizer to output your xml document and keep it Canonical
This gives the output:
<?xml version="1.0" encoding="UTF-8"?>
<doc xmlns="http://example.com/default" xmlns:x="http://example.com/x">
<a a1="1" a2="2">123</a>
<b a3=""3"" y:a1="1" y:a2="2" xmlns:y="http://example.com/y"/>
</doc>
The following example program will output your XML Cannonically to System.out using a XOM Canonicalizer
package com.foo.bar.xom;
import java.io.IOException;
import nu.xom.Builder;
import nu.xom.canonical.Canonicalizer;
import nu.xom.Document;
import nu.xom.ParsingException;
import nu.xom.Serializer;
import nu.xom.ValidityException;
public class App
{
public static void main(String[] args) throws ValidityException, ParsingException, IOException
{
Builder builder = new Builder();
//Serializer serializer = new Serializer(System.out);
Canonicalizer canonicalizer = new Canonicalizer(System.out, Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION);
//this assumes to your xml document is on the classpath in this package as my.xml
Document input = builder.build(App.class.getResourceAsStream("my.xml"), null);
//serializer.write(input);
canonicalizer.write(input);
}
}
Related
Got this error
java.lang.IllegalArgumentException: Unsupported element: net
from this example xml file
<?xml version="1.0" encoding="UTF-8"?>
<net>
<node label="A">
...
</node>
<node label="B">
...
</node>
<node label="C">
...
</node>
</net>
with these java code lines
...
FileInputStream file = new FileInputStream("example.xml");
XMLDecoder decoder = new XMLDecoder(file);
Object decodedResistors = (Object) decoder.readObject();
file.close();
...
Do not use java.beans.XMLDecoder for deserialisation custom XML payloads. It was not designed for that. Read article Long Term Persistence of JavaBeans Components: XML Schema. It contains some example XML payloads which can be deserialised back by XMLDecoder:
<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
<void id="myController" property="owner"/>
<object class="javax.swing.JButton">
<void method="addActionListener">
<object class="java.beans.EventHandler" method="create">
<class>java.awt.event.ActionListener</class>
<object idref="myController"/>
<string>doIt</string>
</object>
</void>
</object>
</java>
If you need to deserialise custom XML use JAXB or Jackson XML. You need to create a POJO model with JAXB annotations:
#XmlRootElement(name = "net")
#XmlAccessorType(XmlAccessType.FIELD)
class Net {
#XmlElement(name = "node")
private List<Node> nodes;
// getters, setters, toString
}
#XmlAccessorType(XmlAccessType.FIELD)
class Node {
#XmlAttribute
private String label;
// getters, setters, toString
}
Example usage:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.util.List;
public class JaxbApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
JAXBContext jaxbContext = JAXBContext.newInstance(Net.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object net = unmarshaller.unmarshal(xmlFile);
System.out.println(net);
}
}
prints:
Net{nodes=[Node{label='A'}, Node{label='B'}, Node{label='C'}]}
See also:
java.lang.IllegalArgumentException: Unsupported element: rss
I need to filter many XML files like this:
<header>
<type>1</type>
<time>today</time>
</header>
<message>
<Event...>
<Counter...>
...
</message>
I need to pick only all the message content whose header.type == 1. I'm reading from multiple files I need to select the message content with type == 1.
I've just little updated your XML-structure to be well-formed. Here are several XML files for testing:
========= one.xml ==========
<root>
<header>
<type>1</type>
</header>
<message>right</message>
</root>
========= two.xml ==========
<root>
<header>
<type>1</type>
</header>
<message>right</message>
</root>
========= three.xml ==========
<root>
<header>
<type>2</type>
</header>
<message>wrong</message>
</root>
And simple code looks like this:
import java.io.File;
import static org.joox.JOOX.$;
public class JooxDemo {
public static void main(String[] args) throws Exception {
final File dirWithXmls = new File("xmls");
for (File xmlFile : dirWithXmls.listFiles()) {
final String message = $(xmlFile).xpath("//header[type='1']/../message").text();
System.out.println(xmlFile.getName() + ", message: " + message);
}
}
}
Output:
one.xml, message: right
three.xml, message: null
two.xml, message: right
As you can see message has been fetched only in case if header is of type 1.
Therefore you can delete .text() and do what you need with message node after simple not-null check.
Edit: here's how I'm loading the XML document, as I used it in Blaise's answer. I'm loading it like this because I want to work with a node, not the whole doc. Even using the whole document I'm still having trouble when loading in this manner.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("[path to doc]/input.xml");
TestClass testClass = (TestClass) unmarshaller.unmarshal(doc);
I've got XML that looks like this:
<test>
<items>
<item type="cookie">cookie</item>
<item type="crackers">crackers</item>
</items>
</test>
And a class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "test")
public class TestClass
{
#XmlPath("items/item/text()")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
// getters, setters omitted
}
The above code will work whether or not I have #XmlElement, and I get an ArrayList containing [cookie, crackers].
If I change the declaration above to
#XmlPath("items/item/#type")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
my ArrayList is empty.
My ultimate goal is to just have attributes so my XML would look like this:
<test>
<items>
<item type="cookie"/>
<item type="crackers"/>
</items>
</test>
Is what I'm trying to do, pull out a list of attributes using XPath, possible, and if so, how?
Thank you.
UPDATE
I have been able to confirm the issue you are seeing (https://bugs.eclipse.org/353763). A fix has been added into our EclipseLink 2.3.1 and 2.4.0 streams and can be obtained from the nightly download page starting August 4th, 2011:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Workaround:
You can workaround this issue by setting your DocumentBuilderFactory to be namespace aware:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("src/forum6907225/input.xml");
testClass = (TestClass) unmarshaller.unmarshal(doc);
marshaller.marshal(testClass, System.out);
You are doing the mapping correctly (see below). Have you included a jaxb.properties file to specify EclipseLink MOXy as your JAXB provider?:
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Test Class
package forum6907225;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "test")
public class TestClass
{
#XmlPath("items/item/#type")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
// getters, setters omitted
}
Demo
package forum6907225;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.Version;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(TestClass.class);
System.out.println(Version.getVersionString());
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum6907225/input.xml");
TestClass testClass = (TestClass) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(testClass, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<test>
<items>
<item type="cookie">cookie</item>
<item type="crackers">crackers</item>
</items>
</test>
Output
2.3.1.qualifier
<?xml version="1.0" encoding="UTF-8"?>
<test>
<items>
<item type="cookie"/>
<item type="crackers"/>
</items>
</test>
My xml file is,
<?xml version="1.0"?>
<type xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="datatype.xsd">
<int>integer</int>
<varchar>varcharacter</varchar>
<double>doubles</double>
</type>
In this xml, I want to set <float></float> as mandatory. But i didn't use this tag. So how to validate the <float> is present or not in my xml file, using xsd with java.? Thanks in advance.
The following can be used to validate your XML against a schema:
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
public class Demo {
public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("datatype.xsd"));
Validator validator = schema.newValidator();
validator.setErrorHandler(new MyErrorHandler());
validator.validate(source);
}
}
For a more detailed example see:
http://bdoughan.blogspot.com/2010/11/validate-jaxb-object-model-with-xml.html
example...
<xml>
<level1>
<level2>
<![CDATA[ Release Date: 11/20/09 <br />View Trailer ]]>
</level2>
</level1>
</xml>
when I use inFeed.getXpath().evaluate("xml/level1/level2", myNodeList);
I get "Release Date:11/20/09 View Trailer"
I was under the impression that the whole point of CDATA is that it preserves whatever mumbo jumbo you care to throw in there.
Am I using the wrong xpath expression? or am just approaching this all wrong?
Not sure what I'm doing differently from you but for me
public class XpathFun
{
public static void main(String[] args) throws Exception
{
String xml = "<xml><level1><level2><![CDATA[ Release Date: 11/20/09 <br />View Trailer ]]></level2></level1></xml>";
InputSource inputSource = new InputSource(new ByteArrayInputStream(xml.getBytes()));
System.out.println(XPathFactory.newInstance().newXPath().evaluate("xml/level1/level2", inputSource));
}
}
results in:
Release Date: 11/20/09 <br />View Trailer