How can I output an XML Node in Freemarker - freemarker

I'm probably abusing Freemarker here, but I'd like to be able to use it to strip a wrapping element from around the outside of an XML document, something like:
<br:wrap xmlns:br="http://demo.tempuri.com/">
<br:borrower>
<br:id>111-11-1111</br:id>
<br:ssn>111-11-1111</br:ssn>
<br:city>Los Angeles</br:city>
<br:first>John</br:first>
<br:last>Smith</br:last>
<br:phone>310-000-0000</br:phone>
<br:state>CA</br:state>
<br:zip>90025</br:zip>
</br:borrower>
</br:wrap>
I want to remove the outer <wrap/> element. It's easy to select the inner document with:
<#ftl ns_prefixes={"D":"http://demo.tempuri.com"}>
<#assign borrower = doc.wrap.borrower>
{ "result" : "${borrower}" }
The problem here is that the 3rd line is going to result in the good old error:
For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), but this has evaluated to a sequence+hash
If I knew exactly what the content and structure of this inner document was I could just walk through it and output the scalar values, but changes all the time. I don't even know the namespaces of all the inner elements (this could even be a problem with the top-level inner object).
I know I could handle this scenario easily with XSLT, but I would much rather find an easy solution in Freemarker.
Any ideas?

${borrower.##markup} should do this.

Related

Find HTML Tags in Properties

My current issue is to find HTML-Tags inside of property values. I thought it would be easy to search with a query like /jcr:root/content/xgermany//*[jcr:contains(., '<strong>')] order by #jcr:score
It looks like there is a problem with the chars < and > because this query finds everything which has strong in it's property. It finds <strong>Some Text</strong> but also This is a strong man.
Also the Query Builder API didn't helped me.
Is there a possibility to solve it with a XPath or SQL Query or do I have to iterate through the whole content?
I don't fully understand why it finds This is a strong man as a result for '<strong>', but it sounds like the unexpected behavior comes from the "simple search-engine syntax" for the second argument to jcr:contains(). Apparently the < > are just being ignored as "meaningless" punctuation.
You could try quoting the search term:
/jcr:root/content/xgermany//*[jcr:contains(., '"<strong>"')]
though you may have to tweak that if your whole XPath expression is enclosed in double quotes.
Of course this will not be very robust even if it works, since you're trying to find HTML elements by searching for fixed strings, instead of actually parsing the HTML.
If you have an specific jcr:primaryType and the targeted properties you can do something like this
select * from nt:unstructured where text like '%<strong>%'
I tested it , but you need to know the properties you are intererested in.
This is jcr-sql syntax
Start using predicates like a champ this way all of this will make sense to you!
HTML Encode <strong>
HTML Decimal <strong>
Query builder is your friend:
Predicates: (like a CHAMP!)
path=/content/geometrixx
type=nt:unstructured
property=text
property.operation=like
property.value=%<strong>%
Have go here:
http://localhost:4502/libs/cq/search/content/querydebug.html?charset=UTF-8&query=path%3D%2Fcontent%2Fgeometrixx%0D%0Atype%3Dnt%3Aunstructured%0D%0Aproperty%3Dtext%0D%0Aproperty.operation%3Dlike%0D%0Aproperty.value%3D%25%3Cstrong%3E%25
Predicates: (like a CHAMP!)
path=/content/geometrixx
type=nt:unstructured
property=text
property.operation=like
property.value=%<strong>%
Have a go here:
http://localhost:4502/libs/cq/search/content/querydebug.html?charset=UTF-8&query=path%3D%2Fcontent%2Fgeometrixx%0D%0Atype%3Dnt%3Aunstructured%0D%0Aproperty%3Dtext%0D%0Aproperty.operation%3Dlike%0D%0Aproperty.value%3D%25%26lt%3Bstrong%26gt%3B%25
XPath:
/jcr:root/content/geometrixx//element(*, nt:unstructured)
[
jcr:like(#text, '%<strong>%')
]
SQL2 (already covered... NASTY YUK..)
SELECT * FROM [nt:unstructured] AS s WHERE ISDESCENDANTNODE([/content/geometrixx]) and text like '%<strong>%'
Although I'm sure it's entirely possible with a string of predicates, it's possibly heading down the wrong route. Ideally it would be better to parse the HTML when it is stored or published.
The required information would be stored on simple properties on the node in question. The query will then be a lot simpler with just a property = value query, than lots of overly complex query syntax.
It will probably be faster too.
So if you read in your HTML with something like HTMLClient and then parse it with a OSGI service, that can accurately save these properties for you. Every time the HTML is changed the process would update these properties as necessary. Just some thoughts if your SQL is getting too much.

How to check whether a value exists in a Ruby structure?

I used to have a series of independent arrays (e.g. name(), id(), description() ). I used to be able to check whether a value existed in a specific array by doing name.include?("Mark")
Now that I moved to a MUCH MORE elegant way to manage different these independent arrays (here for background: How do I convert an Array with a JSON string into a JSON object (ruby)) I am trying to figure out how I do the same.
In short I put all the independent arrays in a single structure so that I can reference the content as object().name, object().id, object().description.
However I am missing now how I can check whether the object array has a value "Mark" in its name structure.
I have tried object.name.include?("Mark") but it doesn't quite like it.
I have also tried to use has_value?but that doesn't seem to be working either (likely because it used to be an hash before I imported it into the structure but right now is no longer a hash - see here: How do I convert an Array with a JSON string into a JSON object (ruby))
Thoughts? How can I check whether object.name contains a certain string?
Thanks.
If you want to find all customers called Mark you can write the following:
customers_named_mark = array_of_customers.select{|c| c.name == 'Mark' }
This will return a potentially empty array.
If you want to find the first customer named Mark, write
customer_named_mark = array_of_customers.detect{|c| c.name == 'Mark' }
This will return the first matching item or nil.

XPath statement to assign an integer variable to the position() function in a powershell script

I am working on a Powershell script which takes as input an XML file, searches for inner text associated with a specific element/tag in that xml file, and returns the position of that element/tag, if it exists, so that the position of that element can be used to replace the inner text with some other data. For example, there may be an xml file that looks like the following . . .
<root>
<category>
<fruit>apple</fruit>
<vegetable>broccoli</vegetable>
<fruit>pear</fruit>
<vegetable>brussel sprouts</vegetable>
<fruit>orange</fruit>
</category>
</root>
So, let's say that in one part of my Powershell script I have some code to find the inner text, "orange". I would like to store in a variable the "position" integer of the "fruit" element that contains "orange" as it's inner text. So, in the above xml file, the position integer would be 3 (or 2 if starting at base zero). How would I write a proper XPath statement to access this 3rd "fruit" location through a variable? Maybe I want to access this location so that I can change "orange" to "banana" or something. I have tried the following with no success . . .
$orangePosition = ??? (I assigned a value of 3 for testing purposes)
root.SelectSingleNode("descendant::category/fruit[position()=$orangePosition]")
I have seen where "position() is assigned an integer value. I want to use a variable in place of the integer because I don't know what index in a particular file the inner text "orange" may be. Is this even possible? I've seen other posts that question similar issues, but none of them seem to work when I apply the solution (if there is one stated) to a powershell script). Any ideas on how this can be resolved?
You could use root.SelectNodes("//category/fruit[. = 'orange']/preceding-sibling::fruit).Count + 1. If you want to do it with XPath alone then use the Evaluate method e.g. root.CreateNavigator().Evaluate("count(//category/fruit[. = 'orange']/preceding-sibling::fruit) + 1"). That should return a C# double value.
It appears that I was making this harder than it needed to be. After extensive research, I have learned that there is really no need to use an index/position integer at all. I can achieve exactly what I need by doing the following . . .
root.SelectNodes('//categories/fruit') | Where-Object {$.InnerText.Contains('orange')} | foreach {$.InnerText -eq 'banana'}
But, I can still think of cases where I might want to actually know the index of the element I would like to edit so that I can exclude other, similar elements that may contain the same innertext. Anyway, this works good enough for my purposes right now. Thanks to all who responded and attempted to help.

Retrieve an xpath text contains using text()

I've been hacking away at this one for hours and I just can't figure it out. Using XPath to find text values is tricky and this problem has too many moving parts.
I have a webpage with a large table and a section in this table contains a list of users (assignees) that are assigned to a particular unit. There is nearly always multiple users assigned to a unit and I need to make sure a particular user is assigned to any of the units on the table. I've used XPath for nearly all of my selectors and I'm half way there on this one. I just can't seem to figure out how to use contains with text() in this context.
Here's what I have so far:
//td[#id='unit']/span [text()='asdfasdfasdfasdfasdf (Primary); asdfasdfasdfasdfasdf, asdfasdfasdfasdf; 456, 3456'; testuser]
The XPath Query above captures all text in the particular section I am looking at, which is great. However, I only need to know if testuser is in that section.
text() gets you a set of text nodes. I tend to use it more in a context of //span//text() or something.
If you are trying to check if the text inside an element contains something you should use contains on the element rather than the result of text() like this:
span[contains(., 'testuser')]
XPath is pretty good with context. If you know exactly what text a node should have you can do:
span[.='full text in this span']
But if you want to do something like regular expressions (using exslt for example) you'll need to use the string() function:
span[regexp:test(string(.), 'testuser')]

XPath concat multiple nodes

I'm not very familiar with xpath. But I was working with xpath expressions and setting them in a database. Actually it's just the BAM tool for biztalk.
Anyway, I have an xml which could look like:
<File>
<Element1>element1<Element1>
<Element2>element2<Element2>
<Element3>
<SubElement>sub1</SubElement>
<SubElement>sub2</SubElement>
<SubElement>sub3</SubElement>
<Element3>
</File>
I was wondering if there is a way to use an xpath expression of getting all the SubElements concatted? At the moment, I am using:
/*[local-name()='File']/*[local-name()='Element3']/*[local-name()='SubElement']
This works if it only has one index. But apparently my xml sometimes has more nodes, so it gives NULL. I could just use
/*[local-name()='File']/*[local-name()='Element3']/*[local-name()='SubElement'][0]
but I need all the nodes. Is there a way to do this?
Thanks a lot!
Edit: I changed the XML, I was wrong, it's different, it should look like this:
<item>
<element1>el1</element1>
<element2>el2</element2>
<element3>el3</element3>
<element4>
<subEl1>subel1a</subEl1>
<subEl2>subel2a</subEl2>
</element4>
<element4>
<subEl1>subel1b</subEl1>
<subEl2>subel2b</subEl2>
</element4>
</item>
And I need to have a one line code to get a result like: "subel2a subel2b";
I need the one line because I set this xpath expression as an xml attribute (not my choice, it's specified). I tried string-join but it's not really working.
string-join(/file/Element3/SubElement, ',')
/File/Element3/SubElement will match all of the SubElement elements in your sample XML. What are you using to evaluate it?
If your evaluation method is subject to the "first node rule", then it will only match the first one. If you are using a method that returns a nodeset, then it will return all of them.
You can get all SubElements by using:
//SubElement
But this won't keep them grouped together how you want. You will want to do a query for all elements that contain a SubElement (basically do a search for the parent of any SubElements).
//parent::SubElement
Once you have that, you could (depending on your programming language) loop through the parents and concatenate the SubElements.

Resources