On groovy templates for jmeter page there is an example I wanted to follow:
String xml = “
<actions>
<action type=”error” info=”itsErrors”/>
<action type="warning" info=”warnWarn”/>
<action type=”info” info=”justLogInfo”/>
</actions>"
XmlParser parser = new XmlParser()
def actions= parser.parseText (xml)
actions.action.each { action ->
println "${action.'#type'}: ${action.'#info'}";
}
At least in my JMeter 5.1 it did not work as posted, but when I fixed quotation marks it did:
String xml = """
<actions>
<action type="error" info="itsErrors"/>
<action type="warning" info="warnWarn"/>
<action type="info" info="justLogInfo"/>
</actions>"""
XmlParser parser = new XmlParser()
def actions= parser.parseText (xml)
actions.action.each { action ->
println "${action.'#type'}: ${action.'#info'}";
}
My question is usage of # mainly, dot and quotes too (.'#type'). I tried web search for Groovy # and found nothing, for JMeter notations found https://jmeter.apache.org/usermanual/functions.html with only one instance of usage:
Example: ${__XPath(/path/to/build.xml, //target/#name)} This will
match all targets in build.xml and return the contents of the next
name attribute
And about variables same link:
Referencing a variable in a test element is done by bracketing the
variable name with '${' and '}'.
Groovy docs page for xml gives other notations:
https://groovy-lang.org/processing-xml.html
def text = '''
<list>
<technology>
<name>Groovy</name>
</technology>
</list>
'''
def list = new XmlParser().parseText(text)
assert list instanceof groovy.util.Node
assert list.technology.name.text() == 'Groovy'
What each notation in "${action.'#type'}: ${action.'#info'}" means?
It isn't a JMeter variable even with ${}, is it?
I managed to keep in working only w/put ', other parts seems necessary: ", ., #, {}, $. I may have put extra in last phrase, some I can explain, but just to be sure I understand it right.
It's GPath syntax used in groovy
The most common way of querying XML in Groovy is using GPath
For XML, you can also specify attributes, e.g.:
a["#href"] → the href attribute of all the a elements
a.'#href' → an alternative way of expressing this
a.#href → an alternative way of expressing this when using XmlSlurper
Related
I need to update an XML file. Its structure is
<product sku="xyz">
...
<custom-attributes>
<custom-attribute name="attrib1">test</custom-attribute>
...
</custom-attributes>
</product>
I want to add a line with a custom-attribute which is multi-valued so the required structure looks like this :
<custom-attributes>
<custom-attribute name="attrib1">test</custom-attribute>
...
<custom-attribute name="new1">
<value>word1</value>
<value>word2</value>
....
</custom-attribute>
</custom-attributes>
I wrote the following python code
precision = {"name" : "new1"}
for sku in soup.find_all('product'):
tagCustoms = sku.find('custom-attributes')
mynewtag = soup.new_tag('custom-attribute', attrs = precision)
tagCustoms.append(mynewtag)
for word in words: # words is a list
mynewtag.insert(1,soup.new_tag('value'))
It works ... except I can't find how to define the content within value's tag .. how to assign each word from words 'list within the same loop ?
I am stuck with this result
<custom-attribute name="new1">
<value></value>
<value></value>
....
</custom-attribute>
</custom-attributes>
I tried this code
for sku in soup.find_all('product'):
tagCustoms = sku.find('custom-attributes')
mynewtag = soup.new_tag('custom-attribute', attrs = precision)
tagCustoms.append(mynewtag)
for word in words: # words is a list
mynewtag.insert(1,soup.new_tag('value'))
mynewtag.value.string = word
but it only add the first word of the list the first value tag.
Many thanks in advance
There are several ways to handle this, but try this one and see if it works.
Change your for loop to:
for word in words:
ntag = soup.new_tag('value')
ntag.string = word
mynewtag.insert(1,ntag)
I have the text of an SVG image file in a string variable in Groovy. I need to modify it to be formed for embedding as a nested SVG. That means:
(1) remove the first line if the first line is an XML declaration that starts as “<?xml”. Or, another way of doing it would be to remove everything up until the start of the SVG tag, i.e., up until “<svg”
(2) within the SVG tag check to see if there is a width=“##” or height=“##” attribute. If so, revise the width and height to be 100%.
How can I do this, e.g, using string replacement or xml parser?
I have tried:
def parsedSVG = new XmlParser().parseText(svg)
if (parsedSVG.name() == “xml”) // remove node here
But the problem is that parsedSVG.name() is the svg tag/node, not the xml definition tag. So it still leaves me unable to tell whether the svg starts with the xml tag.
I have also tried the approaches here GPathResult to String without XML declaration
But my execution environment does not support XML Node Printer and the InvokeHelper call is giving me errors.
As far as string replacement this is what I have tried. But the regular expression doesn’t seem to work. The log shows svgEnd is basically at the end of the svg file rather than being the end of the svg tag as intended...
String sanitizeSvg(String svg) {
String cleanSvg = svg
def xmlDecStart = svg.indexOf("<?xml")
if (xmlDecStart > -1) {
def xmlDecEnd = svg.indexOf("?>")
cleanSvg = cleanSvg.substring(xmlDecEnd+2)
}
def svgStart = cleanSvg.indexOf("<svg")
logDebug("svgStart is ${svgStart}")
if (svgStart > -1) {
def svgEnd = cleanSvg.indexOf('>', svgStart)
logDebug("svgEnd is ${svgEnd}")
if (svgEnd > -1) {
String svgTag = cleanSvg.substring(svgStart, svgEnd-1)
logDebug("SVG Tag is ${svgTag}")
svgTag = svgTag.replaceAll('width="[^"]*', 'width="100%')
svgTag = svgTag.replaceAll('height="[^"]*', 'height="100%')
logDebug("Changed SVG Tag to ${svgTag}")
cleanSvg.replaceAll('(<svg)([^>]*)',svgTag)
}
}
return cleanSvg
}
I need replaceLast() method in the Groovy script - replace the last substring. It is available in Java, but not in Groovy AFAIK. It must work with regex in the same way as the following replaceFirst.
replaceFirst(CharSequence self, Pattern pattern, CharSequence replacement)
Replaces the first substring of a CharSequence that matches the given compiled regular expression with the given replacement.
EDIT: Sorry not being specific enough. Original string is an XML file and the same key (e.g. Name) is present many times. I want to replace the last one.
<Header>
<TransactionId>1</TransactionId>
<SessionId>1</SessionId>
<User>
<Name>Bob</Name>
...
</User>
<Sender>
<Name>Joe</Name>
...
</Sender>
</Header>
...
<Context>
<Name>Rose</Name>
...
</Context>
No idea what replaceLast in Java is...it's not in the JDK... If it was in the JDK, you could use it in Groovy...
Anyway, how about using an XML parser to change your XML instead of using a regular expression?
Given some xml:
def xml = '''<Header>
<TransactionId>1</TransactionId>
<SessionId>1</SessionId>
<User>
<Name>Bob</Name>
</User>
<Sender>
<Name>Joe</Name>
</Sender>
<Something>
<Name>Tim</Name>
</Something>
</Header>'''
You can parse it using Groovy's XmlParser:
import groovy.xml.*
def parsed = new XmlParser().parseText(xml)
Then, you can do a depth first search for all nodes with the name Name, and take the last -1 one:
def lastNameNode = parsed.'**'.findAll { it.name() == 'Name' }[-1]
Then, set the value to a new string:
lastNameNode.value = 'Yates'
And print the new XML:
println XmlUtil.serialize(parsed)
<?xml version="1.0" encoding="UTF-8"?><Header>
<TransactionId>1</TransactionId>
<SessionId>1</SessionId>
<User>
<Name>Bob</Name>
</User>
<Sender>
<Name>Joe</Name>
</Sender>
<Something>
<Name>Yates</Name>
</Something>
</Header>
I'm trying to extract CDATA content from an XML without the using GPath (or) node name. In short, i want to find & retrieve the innerText containing CDATA section from an XML.
My XML look like:
def xml = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<Test1>This node contains some innerText. Ignore This.</Test1>
<Test2><![CDATA[this is the CDATA section i want to retrieve]]></Test2>
</root>'''
From the above XML, i want to get the CDATA content alone without using the reference of its node name 'Test2'. Because the node name is not always the same in my scenario.
Also note that the XML can contain innerText in few other nodes (Test1). I dont want to retrieve that. I just need the CDATA content out of the whole XML.
I want something like below (the code below is incorrect though)
def parsedXML = new xmlSlurper().parseText(xml)
def cdataContent = parsedXML.depthFirst().findAll { it.text().startsWith('<![CDATA')}
My output should be :
this is the CDATA section i want to retrieve
As #daggett says, you can't do this with the Groovy slurper or parser, but it's not too bad to drop down and use the java classes to get it.
Note you have to set the property for CDATA to become visible, as by default it's just treated as characters.
Here's the code:
import javax.xml.stream.*
def xml = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<Test1>This node contains some innerText. Ignore This.</Test1>
<Test2><![CDATA[this is the CDATA section i want to retrieve]]></Test2>
</root>'''
def factory = XMLInputFactory.newInstance()
factory.setProperty('http://java.sun.com/xml/stream/properties/report-cdata-event', true)
def reader = factory.createXMLStreamReader(new StringReader(xml))
while (reader.hasNext()) {
if (reader.eventType in [XMLStreamConstants.CDATA]) {
println reader.text
}
reader.next()
}
That will print this is the CDATA section i want to retrieve
Considering you just have one CDATA in your xml split can help here
def xml = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<Test1>This node contains some innerText. Ignore This.</Test1>
<Test2><![CDATA[this is the CDATA section i want to retrieve]]></Test2>
</root>'''
log.info xml.split("<!\\[CDATA\\[")[1].split("]]")[0]
So in the above logic we split the string on CDATA start and pick the portion which is left after
xml.split("<!\\[CDATA\\[")[1]
and once we got that portion we did the split again and then got the portion which is before that pattern by using
.split("]]")[0]
Here is the proof it works
I'm Trying to deserialize xml data into an object with c#. I have always done this using the .NET deserialize method, and that has worked well for most of what I have needed.
Now though, I have XML that is created by Sharepoint and the attribute names of the data I need to deserialize have encoded caracters, namely:
*space, º, ç ã, :, * and a hyphen as
x0020, x00ba, x007a, x00e3, x003a and x002d respectivly
I'm trying to figure out what I have to put in the attributeName parameter in the properties XmlAttribute
x0020 converts to a space well, so, for instance, I can use
[XmlAttribute(AttributeName = "ows_Nome Completo")]
to read
ows_Nome_x0020_Completo="MARIA..."
On The other hand, neither
[XmlAttribute(AttributeName = "ows_Motiva_x00e7__x00e3_o_x003a_")]
nor
[XmlAttribute(AttributeName = "ows_Motivação_x003a_")]
nor
[XmlAttribute(AttributeName = "ows_Motivação:")]
allow me to read
ows_Motiva_x00e7__x00e3_o_x003a_="text to read..."
With the first two I get no value returned, and the third gives me a runtime error for invalid caracters (the colon).
Anyway to get this working with .NET Deserialize, or do I have to build a specific deserializer for this?
Thanks!
What you are looking at (the "cryptic" data) is called XML entities. It's used by SharePoint to safekeep attribute names and similar elements.
There are a few ways of dealing with this, the most elegant ways to solve it is by extracting the List schema and match the element towards the schema. The schema contain all meta-data about your list data. A polished example of a Schema can be seen below or here http://www.bendsoft.com/documentation/camelot-php-tools/1_5/packets/schema-and-content-packets/schemas/example-list-view-schema/
If you don't want to walk that path you could start here http://msdn.microsoft.com/en-us/library/35577sxd.aspx
<Field Name="ContentType">
<ID>c042a256-787d-4a6f-8a8a-cf6ab767f12d</ID>
<DisplayName>Content Type</DisplayName>
<Type>Text</Type>
<Required>False</Required>
<ReadOnly>True</ReadOnly>
<PrimaryKey>False</PrimaryKey>
<Percentage>False</Percentage>
<RichText>False</RichText>
<VisibleInView>True</VisibleInView>
<AppendOnly>False</AppendOnly>
<FillInChoice>False</FillInChoice>
<HTMLEncode>False</HTMLEncode>
<Mult>False</Mult>
<Filterable>True</Filterable>
<Sortable>True</Sortable>
<Group>_Hidden</Group>
</Field>
<Field Name="Title">
<ID>fa564e0f-0c70-4ab9-b863-0177e6ddd247</ID>
<DisplayName>Title</DisplayName>
<Type>Text</Type>
<Required>True</Required>
<ReadOnly>False</ReadOnly>
<PrimaryKey>False</PrimaryKey>
<Percentage>False</Percentage>
<RichText>False</RichText>
<VisibleInView>True</VisibleInView>
<AppendOnly>False</AppendOnly>
<FillInChoice>False</FillInChoice>
<HTMLEncode>False</HTMLEncode>
<Mult>False</Mult>
<Filterable>True</Filterable>
<Sortable>True</Sortable>
</Field>
<Field>
...
</Field>
Well... I guess I kind of hacked a way around, which works for now. Just replaced the _x***_ charecters for nothing, and corrected the XmlAttributes acordingly. This replacement is done by first loading the xml as a string, then replacing, then loading the "clean" text as XML.
But I wopuld still like to know if it is possible to use some XmlAttribute Name for a more direct approach...
Try using System.Xml; XmlConvert.EncodeName and XmlConvert.DecodeName
I use a simply function to get the NameCol:
private string getNameCol(string colName) {
if (colName.Length > 20) colName = colName.Substring(0, 20);
return System.Xml.XmlConvert.EncodeName(colName);
}
I'm already searching for replace characters like á, é, í, ó, ú. EncodeName doesn't convert this characters.
Can use Replace:
.Replace("ó","_x00f3_").Replace("á","_x00e1_")