How to read a flowscript value when generating an XML for processing in Apache Cocoon 2.2 - apache-cocoon

I have a simple flowscript function that will do this:
cocoon.sendPage("page/index",
{
username : "SomeName"
}
);
In my sitemap.xmap I have this configuration:
<map:pipeline internal-only="true">
<map:match pattern="page/*">
<map:generate src="xml/{1}.xml"/>
<map:transform src="xslt/html.xslt"/>
<map:serialize type="html"/>
</map:match>
</map:pipeline>
I'm using xsl:stylesheet in the html.xslt file to read values from the page.xml-file. It's simple and straight forward (and works as expected).
However: I want to read flowscript values (here: username) in the page.xml-file in order to pass it on to the html.xslt-file. Can this be done by jx:template? (Examples I've found uses that, but they don't work well in Apache Cocoon 2.2, only earlier versions..) If not jx:template's the solution: what else is there?

To read values from flowscript, you can use the JX Template Generator. But unlike Cocoon 2.1 and earlier versions you shouldn't have an explicit reference to org.apache.cocoon.generation.JXTemplateGenerator in the map:generator-section of your sitemap.
For future reference, here's the solution (using Apache Cocoon 2.2 and Maven 3.0.4):
1) Make sure your pom.xml has a dependency to cocoon-template-impl (the JXTemplateGenerator has been moved here)
<dependency>
<groupId>org.apache.cocoon</groupId>
<artifactId>cocoon-template-impl</artifactId>
<version>1.1.0</version>
</dependency>
Verify that your Maven repository contains the library. If not: install it.
2) In flowscript: have a function that will set some values and make a call to .SendPage()/.SendPageAndWait()
cocoon.sendPage("page/index",
{
username : "SomeName"
}
);
3) In sitemap.xmap: when generating XML-files, make sure they have type="jx"
<map:pipeline internal-only="true">
<map:match pattern="page/*">
<map:generate type="jx" src="xml/{1}.jx.xml"/>
<map:transform src="xslt/page.xslt"/>
<map:serialize type="html"/>
</map:match>
</map:pipeline>
4) In your .jx.xml-file: read values like this
<?xml version="1.0" encoding="UTF-8"?>
<jx:template xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
<valuesFromFlowscript>
<!-- Select values from flowscript -->
<username>${username}</username>
</valuesFromFlowscript>
</jx:template>
5) In your .xslt-file you can read the generated value like this
<xsl:value-of select="valuesFromFlowscript/username"/>

Related

Cannot read XML data using XPATH when there is no valid namespace name available [duplicate]

How does XPath deal with XML namespaces?
If I use
/IntuitResponse/QueryResponse/Bill/Id
to parse the XML document below I get 0 nodes back.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3"
time="2016-10-14T10:48:39.109-07:00">
<QueryResponse startPosition="1" maxResults="79" totalCount="79">
<Bill domain="QBO" sparse="false">
<Id>=1</Id>
</Bill>
</QueryResponse>
</IntuitResponse>
However, I'm not specifying the namespace in the XPath (i.e. http://schema.intuit.com/finance/v3 is not a prefix of each token of the path). How can XPath know which Id I want if I don't tell it explicitly? I suppose in this case (since there is only one namespace) XPath could get away with ignoring the xmlns entirely. But if there are multiple namespaces, things could get ugly.
XPath 1.0/2.0
Defining namespaces in XPath (recommended)
XPath itself doesn't have a way to bind a namespace prefix with a namespace. Such facilities are provided by the hosting library.
It is recommended that you use those facilities and define namespace prefixes that can then be used to qualify XML element and attribute names as necessary.
Here are some of the various mechanisms which XPath hosts provide for specifying namespace prefix bindings to namespace URIs.
(OP's original XPath, /IntuitResponse/QueryResponse/Bill/Id, has been elided to /IntuitResponse/QueryResponse.)
C#:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
XmlNodeList nodes = el.SelectNodes(#"/i:IntuitResponse/i:QueryResponse", nsmgr);
Google Docs:
Unfortunately, IMPORTXML() does not provide a namespace prefix binding mechanism. See next section, Defeating namespaces in XPath, for how to use local-name() as a work-around.
Java (SAX):
NamespaceSupport support = new NamespaceSupport();
support.pushContext();
support.declarePrefix("i", "http://schema.intuit.com/finance/v3");
Java (XPath):
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
switch (prefix) {
case "i": return "http://schema.intuit.com/finance/v3";
// ...
}
});
Remember to call
DocumentBuilderFactory.setNamespaceAware(true).
See also:
Java XPath: Queries with default namespace xmlns
JavaScript:
See Implementing a User Defined Namespace Resolver:
function nsResolver(prefix) {
var ns = {
'i' : 'http://schema.intuit.com/finance/v3'
};
return ns[prefix] || null;
}
document.evaluate( '/i:IntuitResponse/i:QueryResponse',
document, nsResolver, XPathResult.ANY_TYPE,
null );
Note that if the default namespace has an associated namespace prefix defined, using the nsResolver() returned by Document.createNSResolver() can obviate the need for a customer nsResolver().
Perl (LibXML):
my $xc = XML::LibXML::XPathContext->new($doc);
$xc->registerNs('i', 'http://schema.intuit.com/finance/v3');
my #nodes = $xc->findnodes('/i:IntuitResponse/i:QueryResponse');
Python (lxml):
from lxml import etree
f = StringIO('<IntuitResponse>...</IntuitResponse>')
doc = etree.parse(f)
r = doc.xpath('/i:IntuitResponse/i:QueryResponse',
namespaces={'i':'http://schema.intuit.com/finance/v3'})
Python (ElementTree):
namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
Python (Scrapy):
response.selector.register_namespace('i', 'http://schema.intuit.com/finance/v3')
response.xpath('/i:IntuitResponse/i:QueryResponse').getall()
PhP:
Adapted from #Tomalak's answer using DOMDocument:
$result = new DOMDocument();
$result->loadXML($xml);
$xpath = new DOMXpath($result);
$xpath->registerNamespace("i", "http://schema.intuit.com/finance/v3");
$result = $xpath->query("/i:IntuitResponse/i:QueryResponse");
See also #IMSoP's canonical Q/A on PHP SimpleXML namespaces.
Ruby (Nokogiri):
puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
'i' => "http://schema.intuit.com/finance/v3")
Note that Nokogiri supports removal of namespaces,
doc.remove_namespaces!
but see the below warnings discouraging the defeating of XML namespaces.
VBA:
xmlNS = "xmlns:i='http://schema.intuit.com/finance/v3'"
doc.setProperty "SelectionNamespaces", xmlNS
Set queryResponseElement =doc.SelectSingleNode("/i:IntuitResponse/i:QueryResponse")
VB.NET:
xmlDoc = New XmlDocument()
xmlDoc.Load("file.xml")
nsmgr = New XmlNamespaceManager(New XmlNameTable())
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
nodes = xmlDoc.DocumentElement.SelectNodes("/i:IntuitResponse/i:QueryResponse",
nsmgr)
SoapUI (doc):
declare namespace i='http://schema.intuit.com/finance/v3';
/i:IntuitResponse/i:QueryResponse
xmlstarlet:
-N i="http://schema.intuit.com/finance/v3"
XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:i="http://schema.intuit.com/finance/v3">
...
Once you've declared a namespace prefix, your XPath can be written to use it:
/i:IntuitResponse/i:QueryResponse
Defeating namespaces in XPath (not recommended)
An alternative is to write predicates that test against local-name():
/*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']
Or, in XPath 2.0:
/*:IntuitResponse/*:QueryResponse
Skirting namespaces in this manner works but is not recommended because it
Under-specifies the full element/attribute name.
Fails to differentiate between element/attribute names in different
namespaces (the very purpose of namespaces). Note that this concern could be addressed by adding an additional predicate to check the namespace URI explicitly:
/*[ namespace-uri()='http://schema.intuit.com/finance/v3'
and local-name()='IntuitResponse']
/*[ namespace-uri()='http://schema.intuit.com/finance/v3'
and local-name()='QueryResponse']
Thanks to Daniel Haley for the namespace-uri() note.
Is excessively verbose.
XPath 3.0/3.1
Libraries and tools that support modern XPath 3.0/3.1 allow the specification of a namespace URI directly in an XPath expression:
/Q{http://schema.intuit.com/finance/v3}IntuitResponse/Q{http://schema.intuit.com/finance/v3}QueryResponse
While Q{http://schema.intuit.com/finance/v3} is much more verbose than using an XML namespace prefix, it has the advantage of being independent of the namespace prefix binding mechanism of the hosting library. The Q{} notation is known as Clark Notation after its originator, James Clark. The W3C XPath 3.1 EBNF grammar calls it a BracedURILiteral.
Thanks to Michael Kay for the suggestion to cover XPath 3.0/3.1's BracedURILiteral.
I use /*[name()='...'] in a google sheet to fetch some counts from Wikidata. I have a table like this
thes WD prop links items
NOM P7749 3925 3789
AAT P1014 21157 20224
and the formulas in cols links and items are
=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(*)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")
=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(distinct?item)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")
respectively. The SPARQL query happens not to have any spaces...
I saw name() used instead of local-name() in Xml Namespace breaking my xpath!, and for some reason //*:literal doesn't work.

Change dynamical directory on file:inbound-channel-adapter

I'm new with Spring and I'm using Citrus Framework.
I'll try to change, dynamically, the inbound-channel-adapter destination variable. This variable is located in properties file and change all the time.
Currently I'm using an AtomicReference and I change its value in java code
In context.xml :
<bean id="targetDir" class="java.util.concurrent.atomic.AtomicReference">
<constructor-arg value="${output.path.temp}"/>
</bean>
<file:inbound-channel-adapter id="fileInboundAdapter" auto-create-directory="false"
channel="fileChannel" directory="file:#targetDir.get()" auto-startup="false"
filename-pattern="*.xml">
<si:poller cron="0 * * * * ?"/>
</file:inbound-channel-adapter>
And in java file :
SourcePollingChannelAdapter fileInboundAdapter = (SourcePollingChannelAdapter)context.getApplicationContext().getBean("fileInboundAdapter");
if (fileInboundAdapter.isRunning()) {
fileInboundAdapter.stop();
#SuppressWarnings("unchecked")
AtomicReference<String> targetDir = (AtomicReference<String>)
context.getApplicationContext().getBean("targetDir", AtomicReference.class);
targetDir.set(strOutPath[0]+"/"+strOutPath[1]+"/"+strOutPath[2]+"/"+strOutPath[3]+"/");
fileInboundAdapter.start();
}
This solution don't works ... someone have any solutions ?
Thanks a lot.
That's true. Because your AtomicReference doesn't have affect to the target directory.
You do this directory="file:#targetDir.get()". It isn't correct at all, because this String will try to be converted to the File object. If you want to use here a SpEL it should be like this:
directory="#{targetDir.get()}"
without any file: prefix.
Anyway it doesn't help because that SpEL is evaluated only once at applicationContext strtup.
Since you are going to change the directory at runtime you should use FileReadingMessageSource.setDirectory from your service. Something like this:
SourcePollingChannelAdapter fileInboundAdapter = (SourcePollingChannelAdapter)context.getApplicationContext().getBean("fileInboundAdapter");
if (fileInboundAdapter.isRunning())
fileInboundAdapter.stop();
FileReadingMessageSource source = (FileReadingMessageSource) context.getApplicationContext().getBean("fileInboundAdapter.source");
source.setDirectory(new File(strOutPath[0]+"/"+strOutPath[1]+"/"+strOutPath[2]+"/"+strOutPath[3]+"/"));
fileInboundAdapter.start();
}
And get rid of that AtomicReference.
From the start you can use property-placeholder for the directory attribute directly.

Sitecore Lucene search indexing and subfolders

How can I make Lucene include results, indexed outside the siteroot eg. stuff based with a root of fx. "/sitecore/content/stuff", but not placed in "/sitecore/content/Home".
Taking a look at SearchManager.cs in "/sitecore modules/LuceneSearch/, the SiteRoot is defined as "SiteCore.Content.Site.Startpath", but making any changes to this file dosent seem to have any affect.
Note:
I am only using the "LuceneResults".ascx & .cs.
----- Question updated, as I narrowed in what the problem might be -----
Im trying to create an index of a specific set of items, for use in a Lucene search.
In web.config, I have specified an index containing:
...
<root>/sitecore/content/Home/Subfolder</root>
...
and that works flawlessly, getting all the subitems when doen a search.
I have then copied exactly the same items to a new location, and updated my web.config as following:
...
<root>/sitecore/content/newSubfolder/Subfolder/Subfolder</root>
...
Now my searches never finds anything!
Does anyone have an idea what could be the problem here.
Note:
- I have rebuild the Search Index db, at every change.
- In "Luke" the index seems fine, and the the search here yields the proper results.
Complete Index:
<index id="faqindex" type="Sitecore.Search.Index, Sitecore.Kernel">
<param desc="name">$(id)</param>
<param desc="folder">__faq</param>
<Analyzer ref="search/analyzer"/>
<locations hint="list:AddCrawler">
<resources type="Sitecore.Search.Crawlers.DatabaseCrawler, Sitecore.Kernel">
<database>master</database>
<root>/sitecore/content/MyContent/Snippets/FAQ</root>
<include hint="list:IncludeTemplate">
<faqblock>{3340AAAE-B2F8-4E22-8B7B-F3EDDB48587E}</faqblock>
</include>
<tags>faqblock</tags>
<boost>1.0</boost>
</resources>
</locations>
</index>
It sounds like you are using the Lucene Search module from Sitecore Marketplace. The code for this module limits the search results to the site root and its children:
public SearchManager(string indexName)
{
SearchIndexName = indexName;
Database database = Factory.GetDatabase("master");
var item = Sitecore.Context.Site.StartPath;
SiteRoot = database.GetItem(item);
}
[...]
public SearchResultCollection Search(string searchString)
{
//Getting index from the web.config
var searchIndex = Sitecore.Search.SearchManager.GetIndex(SearchIndexName);
using(IndexSearchContext context = searchIndex.CreateSearchContext())
{
SearchHits hits = context.Search(searchString, new SearchContext(SiteRoot));
sitecore modules\Lucene Search\SearchManager.cs
Assuming that the "website" node in the sites section of Web.config has startItem="/home", results outside of the "home" hierarchy will not be returned.
If you download the source code for this project, and edit the line that populates SiteRoot to the following, the new items will be returned:
SiteRoote = database.GetItem("/sitecore/content");
Remember to copy the new LuceneSearch.dll to the bin directory of the website project.

Parse XML using Groovy: Override charset in declaration and add XML processing instruction

My initial question have been answered, but that did just open up for further issues.
Example code
Using Groovy 2.0.5 JVM 1.6.0_31
import groovy.xml.*
import groovy.xml.dom.DOMCategory
def xml = '''<?xml version="1.0" encoding="UTF-16"?>
| <?xml-stylesheet type="text/xsl" href="Bp8DefaultView.xsl"?>
|<root>
| <Settings>
| <Setting name="CASEID_SEQUENCE_SIZE">
| <HandlerURL>
| <![CDATA[ admin/MainWindow.jsp ]]>
| </HandlerURL>
| </Setting>
| <Setting name="SOMETHING_ELSE">
| <HandlerURL>
| <![CDATA[ admin/MainWindow.jsp ]]>
| </HandlerURL>
| </Setting>
| </Settings>
|</root>'''.stripMargin()
def document = DOMBuilder.parse( new StringReader( xml ) )
def root = document.documentElement
// Edit: Added the line below
def pi = document.createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="Bp8DefaultView.xsl"');
// Edit #2: Added line below
document.insertBefore(pi, root)
use(DOMCategory) {
root.Settings.Setting.each {
if( it.'#name' == 'CASEID_SEQUENCE_SIZE' ) {
it[ '#value' ] = 100
}
}
}
def outputfile = new File( 'c:/temp/output.xml' )
XmlUtil.serialize( root , new PrintWriter(outputfile))
// Edit #2: Changed from root to document.documentElement to see if that
// would make any difference
println XmlUtil.serialize(document.documentElement)
Problem description
I'm trying to parse a XML-file exported from a third party tool, and before promoting it to stage and production I need to replace certain attribute values. That is all ok, but in addition I must keep the encoding and ref. to the stylesheet.
Since this is pretty static it is totally ok to have both the encoding and the stylesheet ref. definition in a property-file, meaning: I do not need first to find the declarations in the original file.
Encoding in declaration issue
As shown in this answer found here on StackOverFlow you can do
new File('c:/data/myutf8.xml').write(f,'utf-8')
I have also tried
XmlUtil.serialize( root , new GroovyPrintStream('c:/temp/output.txt', 'utf-16'))
but it did not solve the problem for me either. So I have not understood how to override the UTF-value.
Processing instruction issue
Simply, how do I add
<?xml-stylesheet type="text/xsl" href="Bp8DefaultView.xsl"?>
to the output?
Update - This can be done like this
def pi = document.createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="Bp8DefaultView.xsl"');
The processing instruction is being added like this, this guideline showed me, but still I do not get the output.
document.insertBefore(pi, root) // Fails
All issues in this question has been answered in another question I raised, see Groovy and XML: Not able to insert processing instruction
The trick is that I expected
document.documentElement
to contain the processing instruction. But that is wrong, documentElement is:
...This is a convenience attribute that allows direct access to the child node that is the document element of the document...
Where the processing instruction is just another child node. So what I instead had to use was either the LSSerializer or the Transfomer. See Serialize XML processing instruction before root element for details.

Reading/Editing XLIFF using C#

I need to parse an XLIFF file using C#, but I'm having some trouble. These files are fairly complex, containing a huge amount of nodes.
Basically, all I need to do is read the source node from each trans-unit node, do some processing on it, and insert the processed text into the corresponding target node (which will always be present, but empty).
An example of one of the nodes I need to parse would be (the whole file may contain 100s of these):
<trans-unit id="0000000002" datatype="text" restype="string">
<source>Windows Update is not installed</source>
<target/>
<iws:segment-metadata tm_score="0.00" ws_word_count="6" max_segment_length="0">
<iws:status target_content="placeholders_only"/>
</iws:segment-metadata>
<iws:boundary-seg sequence="bs20721"/>
<iws:markup-seg sequence="0000000001">
</trans-unit>
The trans-unit nodes can be buried deep in the files, the header section contains a lot of data. I'd like to use LINQ to XML to read the data, but I'm not having any luck getting it to work. Here's my current code (just trying to read and output the source nodes from the file:
XDocument doc = XDocument.Load(path);
Console.WriteLine("Before loop");
foreach (var transUnitNode in doc.Descendants("trans-unit"))
{
Console.WriteLine("In loop");
XElement sourceNode = transUnitNode.Element("source");
XElement targetNode = transUnitNode.Element("target");
Console.WriteLine("Source: " + sourceNode.Value);
}
I never see 'In loop' and I don't know why, can someone tell me what I'm doing wrong here, or suggest a better way to achieve what I'm trying to do here?
Thanks.
Try
XNamespace df = doc.Root.Name.Namespace;
foreach (XElement transUnitNode in doc.Descendants(df + "trans-unit"))
{
XElement sourceNode = transUnitNode.Element(df + "source");
// and so one, use the df namespace object to qualify any elements names
}
See also http://msdn.microsoft.com/en-us/library/bb387093.aspx.

Resources