FreeMarker: how to access value when multiple namespaces are used? - templates

After I got answer from this topic, I am facing a little more complicated problem now.
Instead of just one namespace, the XML data contains several namespaces:
<?xml version="1.0" encoding="UTF-8" ?>
<user1 xmlns="https://example.com/xyz" xmlns:ns1="https://example2.com/abc">
<ns1:name>Jack</ns1:name>
</user1>
I tried this template:
<#ftl ns_prefixes={"D": "https://example.com/xyz",
"ns1": "https://exampl2.com/abc"}>
<#assign uu= pp.doc["D:user1"]>
${(uu["ns1:name"])}
but got errors back:
The cause of aborting was:
Error when processing this file: data\test1.xml
FreeMarker template error: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a sequence+hash (wrapper: f.e.dom.NodeListModel):
==> (uu["ns1:name"]) [in template "renderer/test.ftlh" at line 4, column 3]
So what is the correct way to get the name which is inside the "ns1"? I've read the FreeMarker documentation but still couldn't figure it out...

The cause of the problem is that you have made typo: exampl2 instead of example2. What FreeMarker (or any XML tool) will look for is an element that belongs to the https://exampl2.com/abc namespace URL, and has name user1, so because of the typo in the URL it will not find any elements.
Note that instead of pp.doc["D:user1"], you can just write pp.doc.user1. That's the point of D, that it can be omitted.

Related

How to store single result from XPath Extractor

I have a HTTP request that return the following XML
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<NS1:obterAtividadesResponse xmlns:NS1="http://www.multiplan.com.br/APL/CLIE/SN/BPM/v1">
<atividades>
<atividade>
<instancia>
<idInstancia>2024</idInstancia>
</instancia>
<idAtividade>12887</idAtividade>
<nomeProcesso>Nota Fiscal ao Pagamento - Resumido</nomeProcesso>
<nomeAtividade>Aprovar Pagamento</nomeAtividade>
<statusAtividade>Received</statusAtividade>
<statusInstancia>Active</statusInstancia>
<dataLimite>2017-09-13T16:08:44.994+00:00</dataLimite>
<snapshot>76</snapshot>
<dadosNegocio>
<name>pedido</name>
<value>4500529987</value>
</dadosNegocio>
</atividade>
</atividades>
</NS1:obterAtividadesResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm trying to extract the idAtividade content with XPath Extractor and save the result on atividadeId variable, but it's saving it's value on atividadeId_1 as you can see in the debug sampler result below:
atividadeId=
atividadeId_1=12887
atividadeId_matchNr=1
I'm using the following xpath query:
//atividades/atividade/idAtividade/text()
Is there a way to make it work as I need it?
Thanks
You should check Return entire XPath fragment instead of text content? checkbox.
It will them take the only text using your expression. See manual.
Also consider using Regular Expression Extractor.
If you have more than one atividade instance in response you can use the following XPath expression to get the first match:
//atividades/atividade[1]/idAtividade/text()
Or alternatively you can select idAtividade node value where nomeAtividade equals to Aprovar Pagamento with something like:
//atividades/atividade[nomeAtividade/text()='Aprovar Pagamento']/idAtividade
However given you have only one atividade instance your expression should work fine, you can test it using "XPath Tester" mode of the View Results Tree listener.
See XPath Tutorial and XPath Language Reference for comprehensive information on XPath syntax, axes, functions, etc.
My mistake, my test had an BeanShell Preprocessor setting the value of atividadeId variable to blank and I didn't know it was being executing after each sampler.

FreeMarker nested data : error while parsing

While processing the below code:
template.process(data, filewriter);
template is an object of freemarker.template.Template class. created by
Configuration cfg = new Configuration(new Version("2.3.23"));
cfg.setDefaultEncoding("UTF-8");
try {
cfg.setDirectoryForTemplateLoading(new File(
"dir/to/templates"));
} catch (IOException e) {
e.printStackTrace();
}
this.template = cfg.getTemplate(path);
filewriter is the FileWriter object
data is a map, which is populated as
key ->"page.title", value ->"Dummy Page title"
key ->"page.body", value ->"Dummy Page Body"
I have used the following template
<html>
<head>
<title>${page.title}</title>
</head>
<body>
<p>This page is generated using freemarker
</p>
<p>
${page.body}
</p>
</body>
</html>
While processing, I am getting following error:
FreeMarker template error:
The following has evaluated to null or missing:
==> page [in template "default.ftl" at line 3, column 10]
----
Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${page.title} [in template "default.ftl" at line 3, column 8]
----
Please suggest what am I doing wrong?
PS: This whole setup works fine, if I change the name "page.title" to "pagetitle" and "page.body" to "pagebody" at both the places.
I really want to use '.' to namespace my values.
As the exception:
FTL stack trace ("~" means nesting-related):
- Failed at: ${page.title} [in template "default.ftl" at line 3, column 8]
freemarker will parse ${page.title} as nested object, you can try to use dataModel
${.dataModel["page.title"]}
Create a public Java class (say, com.example.Page) that has public String getTitle() and public String getBody() methods and put the object into data with name page, or create a Map<String, Object> that contains Map entries with "title" and "body" keys and put that into the data with name page. Then page.title and such will work. Or if you really need to use dot as part of the name, you have to escape the dot so that it knows that you don't mean getting a sub-variable: page\.title.
I create a class named "UrlEntity" which has private member named url with set and get method,then I create a urlEntity named path.I use it by var redir = '${path.getUrl()}'; and I restart my project,then problem solved.

Multiple XML namespaces in xerces

I need to create XML files which contain multiple namespaces.
I create the root element with a default namespace, and add another
namespace ("otherNS") with setAttribute().
The problem is, that when i insert an element (with createElement()) which is prefixed with "otherNS",
xerces adds an empty namespace attribute. when I use createElementNS() and explicitly state the otherNS URI, xerces adds the full URI attribute.
In my understanding of XML namespaces, both is wrong. (Also the examples in
http://www.w3schools.com/Xml/xml_namespaces.asp do not repeat the namespace attributes in each element).
This is an example output:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<company xmlns="http://default.namespace.org/NS" xmlns:otherNS="http://other.namespace.org/ONS">
<otherNS:product xmlns="">Xerces-C</otherNS:product>
<otherNS:category xmlns:otherNS="http://other.namespace.org/ONS" idea="great">XML Parsing Tools</otherNS:category>
<developedBy xmlns="">Apache Software Foundation</developedBy>
</company>
And this is the code:
DOMDocument* doc = impl->createDocument(
X("http://default.namespace.org/NS"),
X("company"),
0);
DOMElement* rootElem = doc->getDocumentElement();
rootElem->setAttribute(
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
DOMElement* prodElem = doc->createElement(X("otherNS:product"));
rootElem->appendChild(prodElem);
DOMText* prodDataVal = doc->createTextNode(X("Xerces-C"));
prodElem->appendChild(prodDataVal);
DOMElement* catElem = doc->createElementNS(
X("http://other.namespace.org/ONS"),
X("otherNS:category"));
rootElem->appendChild(catElem);
My questions are:
Is my usage of the Xerces API correct? Do I maybe have to add the second namespace differently, it seems that xerces does not recognize it.
Are there maybe any features of the DOMLSSerializer class, which change that behaviour, so far I have not found any.
I got the solution on the Xerces mailing list:
Replace:
rootElem->setAttribute(
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
with:
rootElem->setAttributeNS(X("http://www.w3.org/2000/xmlns/"),
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
The reason: the namespace definition itself must be located in the xmlns namespace, so the setAttributeNS() method has to be used.

golang template escape first char

I'm trying to build sitemap XML file with the standard template package.
But the first charset "<" become "&lt ;", and make the XML unreadable for clients.
package main
import (
"bytes"
"fmt"
"html/template"
)
const (
tmplStr = `{{define "indexSitemap"}}<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://www.test.com/sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>https://www.test.com/events-sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>https://www.test.com/gamesAndTeams-sitemap.xml</loc>
</sitemap>
</sitemapindex>{{end}}`
)
func main() {
// Parse the template and check for error
tmpl, parseErr := template.New("test").Parse(tmplStr)
if parseErr != nil {
fmt.Println(parseErr)
return
}
// Init the writer
buf := new(bytes.Buffer)
// Execute and get the template error if any
tmplExErr := tmpl.ExecuteTemplate(buf, "indexSitemap", nil)
if tmplExErr != nil {
fmt.Println(tmplExErr)
return
}
// Print the content malformed
fmt.Println(buf)
}
playground golang
Is that normal?
How can I make it works normaly.
Thanks in advance
Your example shows you're using the html/template package, which auto-escapes text for html usage.
If you want a raw template engine, use the text/template package instead - the html one just wraps it with context-aware escaping.
However, you'll need to make sure by yourself that the texts you output with the raw template engine are XML-safe. You can do this by exposing some escape function to your template, and passing all texts via this function instead of writing them directly.
[EDIT] It looks like a bug in html/template, if you omit the ? from the xml declaration it works okay. But still my advice stands - if it's not html you're better off using the text/template package. Actually, even better, describe the site map as a struct and don't use a template at all, just XML serialization.
Also see issue #12496 on github which confirms they are not planning to fix this.
https://github.com/golang/go/issues/12496
Probably because this is the HTML templating package and you're trying
to produce XML. I suspect that it doesn't know how to parse the
directives with the question mark there.
You probably want to use the text/template package instead, if you're
not going to be taking advantage of any of the HTML auto-escaping
features.

How does one bind namespace prefixes when using QXmlQuery (Qt XQuery)?

I'm attempting to use QXmlQuery to execute an XQuery expression against a document with a declared default namespace.
For discussion:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://namespace.com/ns1">
<node1 attr1="hi"/>
</root>
Now, I have the following to open and query against the document:
QFile temp("my.xml");
temp.open(QIODevice::ReadOnly | QIODevice::Text);
QXmlQuery query;
query.setFocus(&temp);
QXmlResultItems items;
query.setQuery("/root");
query.evaluateTo(&items);
In running this, 'items' never has data in it, since the document is namespaced. Of course, if I remove the default namespace declaration, 'items' does have the correct data, but I don't have that luxury.
I've tried changing the query to: "/prefix:root", and Qt barks a warning like:
No namespace binding exists for the prefix prefix in prefix:root
So namespace binding does exist! But where? I see QXmlNamePool, but it has no mutator methods. I can create a QXmlName with the pool from the query ala:
QXmlName name(query.namePool(), "prefix", "http://namespace.com/ns1");
But it doesn't change anything. I'm at a loss, other toolkits I have used have simple methods to bind prefixes to namespace URIs.
I believe if you would change your query to
...
QXmlResultItems items;
query.setQuery("declare default element namespace \"http://namespace.com/ns1\"; /root");
...
it should return the data.
hope this helps, regards