Match multiple attribute values in html for ant buildscript - html

I want to match mulitple values of a attribute for replacing. for example
<div class="div h1 full-width"></div>
Should produces div, h1 and full-width as seperate matches.
I want to do this to prefix the classes. So instead of div h1 full-width it should be pre-div pre-h1 pre-full-width
The regex I have sofar is
(?<=class=["'])(\b-?[_a-zA-Z]+[_a-zA-Z0-9-]*\b)+
This matches only the first class. This is offcourse because that is the only thing this pattern should match :( I tried to make the lookbehind take more then just class=" but I just end up with it taking everying and leaving nothing to replace.
I want to make a pattern that matches any value individually between the quotes of the class attribute.
I want to do this for an Ant buildscript that processes all files and replaces the class="value1 value2 value3" with a set prefix. Ive done this with little trouble for replacing the classes in css files but ye html seems to be alot trickier.
It is a Ant buildscript. Java regexp package is used to process the pattern. The ant tag used is: replaceregexp
The ant implemtentation of above pattern is:
<target name="prefix-class" depends="">
<replaceregexp flags="g">
<regexp pattern="(?<=class=['"])(\b-?[_a-zA-Z]+[_a-zA-Z0-9-]*\b)+"/>
<substitution expression=".${prefix}\1"/>
<fileset dir="${dest}"/>
</replaceregexp>
</target>

I don't think that you can find n (or in your case 3) different class entries and substitude them in one simple regexp. If you need to do this in ant i think you have to write your own ant task. A better way would be xslt, are you familiar with xslt?

Gave up on Ants ReplaceRegExp and sorted my problem with XSLT to transform xhtml to xhtml.
Following code adds a prefix to all values of a elements class attribute. the xhtml source document must be properly formatted to be parsed.
<xsl:stylesheet version="2.0"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xhtml xsl xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1.dtd"
indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="prefix" select="'oo-'"/>
<xsl:template match="/">
<xsl:apply-templates select="./#*|./node()" />
</xsl:template>
<!--remove these atts from output, default xhtml values from dtd -->
<xsl:template match="xhtml:a/#shape"/>
<xsl:template match="#rowspan"/>
<xsl:template match="#colspan"/>
<xsl:template match="#class">
<xsl:variable name="replace_regex">
<xsl:value-of select="$prefix"/>
<xsl:text>$1</xsl:text>
</xsl:variable>
<xsl:attribute name="class">
<xsl:value-of select="fn:replace( . , '(\w+)' , $replace_regex )"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Can I get value from content dictionary with xpath?

This is an example of a meta tag from which I want to get the pub_date:
<meta name="parsely-page" content='{"title":"Article title","link":"https:\/\/site.com\/category\/article","type":"post","section":"category","image_url":"","author":null,"pub_date":"2009-03-01T14:17:14+00:00","post_id":"article_6463676334","tags":[]}' />
The xpath to get the entire content would be:
//meta[#name="parsely-author"]/#content
Is it possible to get the values of dict keys using xpath?
With XPath 3.1 you can do
//meta[#name="parsely-author"]/parse-json(#content)?pub-date
Sadly, it's very likely that you are using an XPath processor that only supports XPath 1.0 in which case you won't be able to use this unless you find a different processor.
With XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vQ">"</xsl:variable>
<xsl:template match="/">
<xsl:value-of select=
'substring-before(substring-after(//meta[#name="parsely-page"]/#content,
concat($vQ, "pub_date", $vQ, ":", $vQ)), $vQ)'/>
</xsl:template>
</xsl:stylesheet>
When this transformation is performed on this XML document (your meta tag):
<meta name="parsely-page"
content='{"title":"Article title","link":"https:\/\/site.com\/category\/article","type":"post","section":"category","image_url":"","author":null,"pub_date":"2009-03-01T14:17:14+00:00","post_id":"article_6463676334","tags":[]}' />
the wanted result is produced:
2009-03-01T14:17:14+00:00
We can write a single XPath 1.0 expression that evaluates to the wanted string, however we will have to escape quotes and apostrophes in order to avoid errors for their being nested, if unescaped:
substring-before(substring-after(//meta[#name="parsely-page"]/#content,
&apos;"pub_date":"&apos;),
&apos;"&apos;)
Verification using XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vQ">"</xsl:variable>
<xsl:template match="/">
<xsl:value-of select=
'substring-before(substring-after(//meta[#name="parsely-page"]/#content,
&apos;"pub_date":"&apos;),
&apos;"&apos;)'/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the same XML document (above), it evaluates the single XPath 1.0 expression and outputs the wanted, correct result:
2009-03-01T14:17:14+00:00

is it possible to disable-output-escaping twice in XSLT

I have XML that has encoded HTML data. I am trying to render the data but can't seem to figure out how. Best I can tell is I need to disable-output-escaping="yes" twice but not sure how to do that.
For example, this is a snippet of my XML:
<root>
<node value="&lt;b&gt;body&lt;/b&gt;" />
</root>
My XSLT is outputting HTML. Here is the rendered output (the HTML source) with various options
<xsl:value-of select="#value" /> outputs &lt;b&gt;hi&lt;/b&gt;
<xsl:value-of select="#value" disable-output-escaping="yes" /> outputs <b>hi</b>
I would like it to output <b>hi</b> to the HTML source so its actually rendered as a bolded hi. Does that make sense? Is that possible?
Escaping is the process of turning < into <. If you disable escaping, it will leave < as <. What you want to achieve is to turn < into <, which would normally be called "unescaping".
In the normal course of events, a parser performs unescaping, while a serializer performs escaping. So if you want to unescape characters, you need to put them through a parsing process, which means you need to take the content of the #value attribute and put it through an operation like fn:parse-xml-fragment() in XPath 3.0, or an equivalent extension function in your chosen processor.
Assuming Sharepoint as a Microsoft .NET product uses XslCompiledTransform you could try to implement the unescaping and parsing with extension "script" (C# or VB or JScript.NET code embedded in XSLT) as follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="msxsl mf">
<msxsl:script language="C#" implements-prefix="mf">
<msxsl:using namespace="System.IO"/>
public string Unescape(string input)
{
XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = input;
return frag.InnerText;
}
public XPathNavigator ParseXml(string xmlInput)
{
using (StringReader sr = new StringReader(xmlInput))
{
return new XPathDocument(sr).CreateNavigator();
}
}
</msxsl:script>
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node">
<div>
<xsl:copy-of select="mf:ParseXml(mf:Unescape(#value))" />
</div>
</xsl:template>
</xsl:stylesheet>
If you have access to an XSLT processor (like any version of Saxon 9.7 or Exselt or the latest Altova or XmlPrime) supporting the XPath 3 functions parse-xml and parse-xml-fragment you can write that template without extension functions (in a version="3.0" stylesheet) as
<xsl:template match="node">
<div>
<xsl:copy-of select="parse-xml(string(parse-xml-fragment(#value)))"/>
</div>
</xsl:template>
Output your result with disable-output-escaping, then treat it again in another XSL with disable-output-escaping.

xslt completely remove duplicates from string

I have a variable containing non-numerical values, and I need to completely remove duplicate entries from this string using XSLT:
$string = a,b,c,c,d,d,e,f,g
needs to become: $newstring = a,b,e,f,g
An alternative option would be to compare the two variables and ignore/remove the overlapping entries.
$stringA = a,c
$stringB = a,b,c,d,e,f
needs to become:
$newstring = b,d,e,f
Concatenating the variables is straightforward but I need the opposite of that!
Please help,
XSLT is designed to process XML, not strings. XSLT 1.0 in particular is a poor tool for manipulating text.
IMHO, the best way to proceed here is to convert the problem to XML first. If you're using libxslt (as xsltproc does), this is quite easy to do using an extension function:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="stringA">a,c,g</xsl:param>
<xsl:param name="stringB">a,b,c,d,e,f</xsl:param>
<xsl:variable name="setA" select="str:tokenize($stringA, ',')" />
<xsl:variable name="setB" select="str:tokenize($stringB, ',')" />
<xsl:template match="/">
<test>
<xsl:for-each select="$setA[not(.=$setB)] | $setB[not(.=$setA)]">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
</test>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<test>g,b,d,e,f</test>

Why is there xmlns in my html output

In the html output file from an XSLT process (using saxon9he), there have been 155 occurrences of xmlns:fn="http://www.w3.org/2005/xpath-functions" inserted into a variety of tr elements
The part of xsl that uses xpath-functions is
<xsl:if test="(string(#hideIfHardwareIs)='') or (not(fn:matches(string($input_doc//inf[#id='5'), string(#hideIfHardwareIs), 'i')))">
unless I am reading it wrong, matches takes 3 arguments, a string, another string and then a flag in which case this is case-insensitive.
What I don't undestand is that the tr elements that are showing up with the xmlns arent close to the portion or xsl that the matches() function is done at.
The XSL file I am working with is 2100 lines and the XML file it parses is 12800 lines. So I don't think I can share it easily. I've inherited this and need to (at this time) maintain it.
What are somethings i can look for within the XSL that would insert the xmlns into the html output?
Those functions do not need to be prefixed.
Remove the xmlns:fn="http://www.w3.org/2005/xpath-functions" from your xsl:stylesheet and remove the fn: prefix from the xpath functions.
Examples:
XML Input
<foo>test</foo>
XSLT 2.0 #1
<xsl:stylesheet version="2.0" xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:if test="fn:matches(.,'^t')">
<bar><xsl:value-of select="."/></bar>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output
<bar xmlns:fn="http://www.w3.org/2005/xpath-functions">test</bar>
XSLT 2.0 #2
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:if test="matches(.,'^t')">
<bar><xsl:value-of select="."/></bar>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output
<bar>test</bar>

Using XSLT as xhtml link extractor

I'm starting using XSLT and write this scipt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="span[#class='thumb']" >
Link: <xsl:value-of select="$base" /><xsl:value-of select="a/#href" />
</xsl:template>
<xsl:template match="/">
Base href: <xsl:value-of select="$base" />
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
And using this command:
xsltproc --html --param base "'http://example.com'" lista.xslt test.html
I need to get list of Links, but I get whole page on output. What's wrong? How can I get it works?
There are some default templates which are unseen here. The really easy way to resolve it is to just explicitly limit to the span elements you're matching as below. Otherwise, you can override the default templates.
<xsl:template match="/">
Base href: <xsl:value-of select="$base" />
<xsl:apply-templates select="//span[#class='thumb']" />
</xsl:template>
There's a default template that matches essentially everything if you let it. Your 4th last line calls that template.
That's part of the problem. The rest can probably be taking care of by matching just the stuff you're looking for, directly in the top-level template.