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.
Related
What I want to do?
I'm working on a Apache Cocoon Project and want to find solution to return paramaters in HTML pages.
I need to get the parameter, which has ArrayList type, and use it in HTML page in order to fill a table. How can I do this? Is it correct to set a request parameter? If yes, then how to use it inside HTML code? If no, then how to return the parameter correctly?
ActionClass.java
public class ActionClass implements Action, ThreadSafe{
public Map act(Redirector rdrctr, org.apache.cocoon.environment.SourceResolver sr, Map map, String string, Parameters params) throws Exception {
// READ REQUEST
Request request = ObjectModelHelper.getRequest(map);
// DO SOMETHING XQUERY VIA BASEX, SPARQL RDFSTORE WHATEVER
ArrayList<ResultBean> results = xquery();
Map sitemapParams = new HashMap();
// SET REQUEST PARAMETER
request.setAttribute("results",results);
return sitemapParams;
}
}
ResultBean.java
package com.kiddo.grlegislation;
public class ResultBean {
private String id;
private String title;
private String type;
public void setId(String i){
this.id = i;
};
public void setTitle(String t){
this.title = t;
};
public String getId(){
return this.id;
};
public String getTitle(){
return this.title;
};
}
First of all, are you sure that you need an Action? Actions are meant to act somehow (update something in the database, invoke a web service, etc). If you just need to generate content, a Generator class could be a better fit for you...
Anyway... How could you return something from an Action into HTML? Lets see it with an example:
Action class: because it extends Action, it must return a Map. Just add there whatever data you need to pass to your HTML:
package com.stackoverflow;
public class ActionClass extends Action {
public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters params) {
Map<String, String> sitemapParams = new HashMap<String, String>();
sitemapParams.put("myVariable", "hello world!");
return sitemapParams;
}
}
sitemap.xmap: in your sitemap file, you can access any data returned by the Action, by placing it's key between brackets. Then you can pass it to your HTML generator:
<map:components>
<map:actions>
<map:action name="myAction" src="com.stackoverflow.ActionClass" />
</map:actions>
</map:components>
...
<map:match ...>
<map:generate ... />
<map:act type="myAction">
<map:transform src="myTransformation.xsl">
<map:parameter name="something" value="{myVariable}"/>
</map:transform>
</map:act>
<map:serialize .../>
</map:match>
myTransformation.xsl: your XSLT file should read the data and embed it into your HTML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="something" select="'default value if you wish to specify one'"/>
<xsl:template match="xxx">
<html><body>...
<xsl:value-of select="$something" />
...</body></html>
</xsl:template>
</xsl:stylesheet>
You can get more information about Actions, Generators and the sitemap in this page. It's from Apache Cocoon 2.1 documentation, but it also applies to 2.2.
Alternative approach, with a Generator:
Generator class: this file builds a XML document, which is then passed into the pipeline. You could have something like this:
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
...
public class GeneratorClass extends AbstractGenerator {
private String foo;
#Override
public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params) throws ProcessingException, SAXException, IOException {
super.setup(resolver, objectModel, src, params);
// you can read input parameters in here:
foo = params.getParameter("someParameter");
}
public void generate() throws IOException, SAXException, ProcessingException {
ArrayList<ResultBean> beans = xQuery(foo);
// Let's build the XML document. I'll do it by manually appending text strings,
// but there is no need, we could just use Xstream or any similar library
StringBuilder xml = new StringBuilder();
xml.append("<results>");
// Iterate through the array list...
for (ResultBean b : beans) {
xml.append("<result>");
xml.append("<id>").append(b.getId()).append("</id>");
xml.append("<title>").append(b.getTitle()).append("</title>");
xml.append("</result>");
}
// ... and we end the XML string
xml.append("</results>");
// Return the XML to Cocoon's pipeline
XMLReader xmlreader = XMLReaderFactory.createXMLReader();
xmlreader.setContentHandler(super.xmlConsumer);
InputSource source = new InputSource(new StringReader(xml.toString()));
xmlreader.parse(source);
try {
this.finalize();
} catch (Throwable e) {
}
}
}
Sitemap.xmap: you just need to call your generator, and then apply your XSLT to the generated XML:
<map:components>
<map:generators>
<map:generator type="myGenerator" src="com.stackoverflow.GeneratorClass" />
</map:generators>
/<map:components>
<map:generate type="myGenerator">
<!-- if you need to pass input data to the generator... -->
<map:parameter name="someParameter" select="{request-param:something}" />
</map:generate>
<map:transform src="myTransformation.xsl" />
<map:serialize type="html"/>
myTransformation.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head></head>
<body>
<table>
<xsl:for-each select="results/result">
<tr>
<td><xsl:value-of select="id/text()"/></td>
<td><xsl:value-of select="title/text()"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
You can get more info about Cocoon generators here. Once again, it's an official tutorial for Cocoon 2.1, but it's also valid for Cocoon 2.2.
I'm trying to do something like this and it seems to work:
<map:pipeline id="pd-version">
<map:match pattern="pd/*/*">
<map:aggregate element="foo">
<map:part src="cocoon:/version-{1}-{2}.xml"/>
<map:part src="http://localhost:8888/GRLegislation/pd/{1}/{2}/data.xml"/>
</map:aggregate>
<map:transform src="legislation_updated.xslt" type="xslt-saxon"/>
<map:transform src="legislation.xslt" type="xslt-saxon">
</map:transform>
<map:serialize type="xhtml"/>
</map:match>
</map:pipeline>
<map:pipeline>
<map:match pattern="version-*-*">
<map:generate type="versiongen">
<map:parameter name="type" value="pd"/>
<map:parameter name="year" value="{1}"/>
<map:parameter name="id" value="{2}"/>
</map:generate>
<map:serialize type="xml"/>
</map:match>
</map:pipeline>
Parameters are not loaded correctly from <map:part> to <map:match>. Also I have some XSLT issues, because now we have a different root of XML.
I have a web-service, defined by writing its WSDL and underlaying XSD, and the java server code classes / java bindings were generated using JAXB/xjc.
Everything looks fine service is running properly... but for every request (looking well-formed after receiving when looking on log-output) the nested elements seem to be always null when accessing through my java code.
Can someone figure out why customerId.getCustomer() always returns null?
My XSD (partly):
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:tip="http://example.org/tip" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.org/tip/pro">
<complexType name="id">
<attribute name="id" type="int" use="required"/>
<attribute name="name" type="string" use="optional"/>
</complexType>
<complexType name="customer_id">
<sequence>
<element name="customer" type="tip:id" minOccurs="0"/>
</sequence>
</complexType>
<element name="get_customer_request" type="tip:customer_id"/>
</schema>
The generated class CustomerId:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer_id", propOrder = {"customer"})
public class CustomerId {
protected Id customer;
public Id getCustomer() {
return customer;
}
public void setCustomer(Id value) {
this.customer = value;
}
}
The generated class for Id look similar, I don't think there is something special.
In my request handler I got the following extract:
Handler:
JAXBElement<?> request = requestHandler.unmarshallRequest(inputStream);
Object jaxbClass = request.getDeclaredType();
expectedClass = CustomerId.class;
// next line does not throw exception with given XML
if (jaxbClass != expectedClass) throw new IllegalArgumentException();
CustomerId customerId = (CustomerId)request.getValue();
if (customerId == null) {
logInfo("customerId: null");
} else if (customerId.getCustomer() == null) {
// this is the part that always will be executed... why?
logInfo("customerId.customer: null");
} else {
logInfo("customer id: " + customerId.getCustomer().getId());
// return mbean.getCustomer(customerId);
}
And finally an example request XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<customer id="0" name="help"/>
</m:get_customer_request>
I stripped out SOAP envelope and body tags, since this is not causing any trouble.
Can anyone see, what I am doing wrong? (I am pretty sure, I do...)
Thanks for your effords!
PART 1
When I create a new Id and set customerId.customer with this, the full
output is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<get_customer_request xmlns="example.com/tip/pro">
<customer name="xy" id="1"/>
</get_customer_request>
Based on this information it appears that your JAXB mappings expect the customer element to be in the example.com/tip/pro namespace, and your request document should be:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<m:customer id="0" name="help"/>
</m:get_customer_request>
PART 2
When putting m: prefix to customer element in my request, the parser
complains that he found m:customer and expected customer.
This means that your XML schema does not match your mappings. If you expect the customer element to be in the namespace you can change your XML schema to the following:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns:tip="http://example.org/tip"
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/tip/pro"
elementFormDefault="qualified">
...
</schema>
For more information on JAXB and namespaces see:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
I'm attempting to build a Jersey web service which will take in data, format it into an XML document, then pass it to another service. I realize that Jersey does have XML support, but I'm having a bit of trouble implementing it due to the required XML structure for the project. The desired output looks something like this:
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
My issue arises in that there are a variable number of <d> and <row> elements, which will be determined based on the data passed in. I know that I can format a simple table with #XmlRootElement above the class which handles the data, but this may only be useful for my <root-element> since the element only gets populated with other elements. I know I'll need to use some sort of loop to create each <row>, but I'm not sure how I can create each <d> element with different data in each field. Any suggestions?
You can use a Java model with JAXB (JSR-222) annotations to support your use case. Elements that can occur more than once will correspond to List properties in your Java model. Below is an example of how your document could be mapped.
Table
We will use the #XmlElementWrapper annotation to add a grouping element, and the #XmlElement annotation to set the element name for the items in the collection.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="root-element")
#XmlAccessorType(XmlAccessType.FIELD)
public class Table {
#XmlElementWrapper(name="table")
#XmlElement(name="row")
private List<Row> rows;
}
Row
If the name of your property/field matches the name of the resulting XML element then you do not require any annotations.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Row {
private List<String> d;
}
Demo
Below is a standalone example to prove that the mapping works:
package forum11543081;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Table.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11543081/input.xml");
Table table = (Table) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(table, System.out);
}
}
input.xml/Output
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
</root-element>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-15.html
If you want to use the default Jersey/JAXB marshalling into XML, you would build a schema reflecting the structure you have indicated which includes collections (unbounded elements) and generate (using xjc) the corresponding java classes. The response from your restful service would be the type associated with the root element and you would build the structure as part of the service. The unbounded elements are rendered as java lists so they can be of arbitrary number of elements. In the code you would just .add(element) as necessary. Something like:
<schema ...>
...
<element name="root-element">
<complexType>
<sequence>
<element name="table" type="tns:TableType" />
</sequence>
</complexType>
</element>
<complexType name="TableType">
<sequence>
<element name="row" minOccurs="0" maxOccurs="unbounded" type="tns:RowType" />
</sequence>
</complexType>
<complexType name="RowType">
<sequence>
<element name="d" minOccurs="0" maxOccurs="unbounded" type="string" />
</sequence>
</complexType>
</schema>
The alternate approach would be (as mentioned by TedTrippin) using stax (streaming processor) to build up the xml document tag by tag with loops in appropriate places and returning the final result.
What I've ended up doing:
Since I had code from another project I could re-use for looping through the XML building, I decided to build the XML in a document, then write that document to a string like so:
public class XmlHandler{
public static String buildXml(){
String xmlString="";
//Create XML Document
DocumentBuilderFactory docfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docbuil = null;
docbuil = docfac.newDocumentBuilder();
Document doc = docbuil.newDocument();
//Build XML Elements
Element root = doc.createElement("root-element");
doc.appendChild(root);
Element table = doc.createElement("table");
root.appendChild(table);
//Hard coded data here for testing purposes.
String[][]array={
{"data1", "data2", "data3"},
{"data4", "data5", "data6"}
};
Text text = null;
Element d = null;
Element row = null;
for(String[] line : array)
{
row=doc.createElement("row");
table.appendChild(row);
for(String label : line)
{
d = doc.createElement("d");
row.appendChild(d);
text = doc.createTextNode(label);
d.appendChild(text);
}
}
}
//Write Document to String
DOMImplementationLS domImplLS = (DOMImplementationLS) doc.getImplementation();
LSSerializer serializer = domImplLS.createLSSerializer();
serializer.getDomConfig().setParameter("format-pretty-print", true);
LSOutput output = domImplLS.createLSOutput();
output.setEncoding("UTF-8");
StringWriter sw = new StringWriter();
output.setCharacterStream(sw);
serializer.write(doc, output);
xmlString = sw.toString();
return xmlString;
}
}
While the hard coded String array won't be around for long, just until I find out what data types I need to pass in, this class is doing the trick just fine.
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);
}
}
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