I couldn't figure out how to allow multiple namespaces, since NAnt seem to doesn't have any concept of a params array parameter for functions. I thought about this and I'm not entirely sure why you'd want to specify multiple namespaces anyway (I was just trying to replicate the existing <xmlpeek> task), so I gave up and just allowed you to specify one namespace.
If you wanted to replicate the example given in the <xmlpeek> help, this is how you'd do it:
<echo message="${xml::get-value-using-ns('test.xml', '/x:configuration/x:appSettings/x:[EMAIL PROTECTED] = ''server'']/@value', xml::get-ns('x', ' http://www.gordic.cz/shared/project-config/v_1.0.0.0'))}"/>
xml::get-ns(string prefix, string uri)
Will create you a namespace object from the prefix and uri that you pass in
xml::get-value-using-ns(string xmlFile, string xPath, XmlNamespace xmlNamespace)
Will get a value using the namespace you pass in.
This is the code for it:
public static string GetXmlValueUsingNamespaces(string xmlFile, string xPath, XmlNamespace xmlNamespace)
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlFile);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xd.NameTable);
XmlNamespace xns = new XmlNamespace();
if (xmlNamespace.IfDefined && !xmlNamespace.UnlessDefined)
{
nsMgr.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri);
}
try
{
string s = xd.SelectSingleNode(xPath, nsMgr).Value;
}
catch(NullReferenceException err)
{
throw new BuildException("The specified node could not be found", err);
}
return s;
}
[Function("get-ns")]
public static XmlNamespace GetNamespace(string prefix, string uri)
{
XmlNamespace xn = new XmlNamespace();
xn.Prefix = prefix;
xn.Uri = uri;
return xn;
}
Let me know if this does the job. If so, I'll adapt the other functions to use namespaces to have similar alternatives when I have more time.
Cheers
John
On 30/01/06, John Ludlow <[EMAIL PROTECTED]> wrote:
Actually, scratch that. It seems that it needs to implement IConvertible if it needs to pass to that function. I'll carry on looking at it, and let you know how I get on.
On 30/01/06, John Ludlow <[EMAIL PROTECTED]> wrote:Hi, Martin,
I've knocked together the following two functions. I haven't tested them yet, but they don't exactly work ideally anyway. I want to be able to have a params array of XmlNamespace[] as the last parameter, but it won't let me do that because, apparently, XmlNamespace needs to implement IConvertible. I've taken a quick gander at this and it looks like implementing this might be a big job.
If I can get that working, the syntax would be something like
${xml::get-value-using-ns('test.xml', '/data/something', xml::get-ns('x', ' http://x-uri'), xml::get-ns('y', ' http://y-uri'))}
OTOH, there might be a good reason that XmlNamespace doesn't support this interface.
What do you think?
[Function("get-value-using-ns")]
public static string GetXmlValue(string xmlFile, string xPath, XmlNamespace xmlNamespace)
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlFile);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xd.NameTable);
XmlNamespace xns = new XmlNamespace();
if (xmlNamespace.IfDefined && !xmlNamespace.UnlessDefined)
{
nsMgr.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri);
}
string s = xd.SelectSingleNode(xPath, nsMgr).Value;
return s;
}
[Function("get-ns")]
public static XmlNamespace GetNamespace(string prefix, string uri)
{
XmlNamespace xn = new XmlNamespace();
xn.Prefix = prefix;
xn.Uri = uri;
return xn;
}
BTW, I couldn't get your example of a namespace working. It kept complaining about the version number in the preprocessor. I copied the one from the NAnt help which worked ok.On 30/01/06, Martin Aliger < [EMAIL PROTECTED]> wrote:Hello,
I like it very much. Expecially functions. Bad luck I personally need
namespace support, since mine xmls have some namespace in it. And even
default namespace needs specification in xpaths :-(
Maybe there could be some elegant way how to pass namespace mapping into
functions. New type come to my mind. It could be elegant perhaps.
btw: does nant functions support overloading and/or default parameter value?
bool xml::value-exists (string xmlFile, string xPath,
NamespaceMap nsMap = null)
===
Here is typical use of namespace map (taken from my real script):
<xmlpeek
file="${prjconfig}"
xpath="x:Project/x:ExeName"
property="newexe"
failonerror="true"
failonnonode="false"
>
<namespaces>
<namespace
prefix="x" uri="http://www.gordic.cz/shared/project-config/v_1.0.0.0 "/>
</namespaces>
</xmlpeek>
And sample xml file is here (changed to protect innocent):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns=" http://www.gordic.cz/shared/project-config/v_1.0.0.0">
<Name>foo</Name>
<Description>foobar</Description>
<ExeName>footest</ExeName>
</Project>
===
I missed xml-foreach once too, but nowadays I do not need it. Still it could
be worth addition to NAnt(Contrib) tasks!
Martin Aliger
> -----Original Message-----
> From: [EMAIL PROTECTED]
> [mailto:[EMAIL PROTECTED] ] On
> Behalf Of John Ludlow
> Sent: Tuesday, January 24, 2006 7:19 PM
> To: nant-developers@lists.sourceforge.net;
> nantcontrib-developer@lists.sourceforge.net
> Subject: [nant-dev] New XML tasks and functions
>
> Hi, there
>
> Recently I mentioned on the NAnt Dev list that I have been
> working on XML-handling functionality. I've got further than
> I had last time, so I thought about summarising what I've
> done here (I'm also happy to share the code and/or binaries,
> so if you want either of these to check it out for yourself,
> just email me at [EMAIL PROTECTED]). I've put my code
> into an assembly, tested it, and am currently contemplating
> writing some unit tests for it.
> Before I go through that, though, I wanted to get some
> feedback and a better idea of what people thought of this.
>
> This is what I have so far:
>
> Functions
> =======
>
> bool xml::value-exists (string xmlFile, string xPath)
> --------------------------------------------------------------
> -----------
> Tests whether a specified xpath query returns a value. If
> this gets a NullReferenceException (usually means an XML
> value doesn't exist).
>
> string xml::get-value (string xmlFile, string xPath)
> --------------------------------------------------------------
> ---------
> Gets a value returned by an XPath query. Throws an error if
> the node specified in the xpath is not found. Duplicates
> some of <xmlpeek>'s functionality, but enables you to use
> ${x} syntax to get an xml value.
> This fits in with the idea that being able to use this
> functionality is nicer if all you want is information.
> However, it does not support namespaces like xmlpeek does.
>
> string xml::get-value-without-failing (string xmlFile, string xPath)
> --------------------------------------------------------------
> -----------------------------
> Gets a value returned by an XPath query. As above, but does
> not an error if the node specified in the XPath is not found,
> instead returning a blank string.
>
> string xml::get-xml (string xmlFile, string xPath) Gets the
> InnerXml property of an XML node returned by an XPath query.
>
> int xml::count (string xmlFile, string xPath) Gets the number
> of nodes in a document that match the specified XPath query.
>
> Tasks
>
> xml-foreach
> =========
> Iterates through an XML document. I thought about making
> this part of the ForEach task, but there seemed to be a
> significant difference in functionality, especially
> concerning getting the details of each item in the
> iteration. For this reason, I decided to make a separate task.
> The syntax is explained below:
>
> Attributes:
> ========
> - file: string. XML file to iterate
> - xpath: string. XPath. The task will essentially iterate
> through the list of nodes that match this query.
> - index: string. Optional. A property that can contain
> the index of a returned result in the node list. You can use
> this to find this node in the doument again, especially
> useful for nested loops. By default doesn't set any property.
>
> Nested elements:
> =============
> - do: The tasks to run on each iteration
>
> - xmlpropertybinding: Runs XPath queries on the result,
> putting the returned value in a property.
>
> - xmlpropertybinding/get
>
> Attributes
> -----------------------------------------------
> - xpath: string. The XPath query to run
> - property: string. The property to populate with the value
> returned by the XPath query.
> - failonempty: bool. Optional. Specifies whether or not
> the script should fail if the node specified in the XPath
> cannot be found.
> Default is false.
>
> Example:
>
> Assuming you have the following in a file called test.xml
>
> <root>
> <data name="bob" age="44">
> <children>
> <child name="tom" age="3"/>
> <child name="dick" age="1"/>
> <child name="harry" age=".5"/>
> </children>
> </data>
> <data name="bill" age="15">
> </data>
> <data name="ted" age="372">
> <children>
> <child name="sarah" age="4"/>
> </children>
> </data>
> <data name="fred" age="23">
> <children>
> <child name="phil" age="5"/>
> </children>
> </data>
> </root>
>
> ...and you have the following in your build script:
>
> <xml-foreach file="test.xml" xpath="/root/data" index="i">
> <xmlpropertybinding>
> <get xpath="@name" property="name"/>
> <get xpath="@age" property="age"/>
> <get xpath=" children/child/@name " property="child"/>
> </xmlpropertybinding>
> <do>
> <echo message="#${i} ${name} is ${age} years old"/>
> <xml-foreach file=" test.xml"
> xpath="/root/data[${i}]/children/child">
> <xmlpropertybinding>
> <get xpath="@name" property="childname"/>
> <get xpath="@age" property="age"/>
> </xmlpropertybinding>
> <do>
> <echo message="${name}::${childname} is ${age} years old"/>
> </do>
> </xml-foreach>
> </do>
> </xml-foreach>
>
> ...you'll get this returned:
>
> [echo] #1 bob is 44 years old
> [echo] bob::tom is 3 years old
> [echo] bob::dick is 1 years old
> [echo] bob::harry is .5 years old
> [echo] #2 bill is 15 years old
> [echo] #3 ted is 372 years old
> [echo] ted::sarah is 4 years old
> [echo] #4 fred is 23 years old
> [echo] fred::phil is 5 years old
>
> xmlpoke2
> =======
> Like <xmlpoke>, but writes a whole structure, rather than a
> single value. Especially useful if you want to dynamically
> create an XML file in your script. While this is currently
> possible in NAnt using a combination of <echo> (or <copy> if
> you have a template copy of the
> file) and <xmlpoke>, it involves a lot of escape characters.
> This new task allows you to dynamically create your xml
> structure in your build script and write it all in one go.
>
> Attributes
> --------------
> - file: string. The xml file to write to.
> - xpath: string. Where to write to in the document. To
> write at the root, use "/"
> - create: bool. Optional. Specifies whether the task
> should create the file if it can't find it. Defaults to true.
>
> Nested Elements
>
> - data: the XML data to write. You should put this into a CDATA
> element.Example:
>
> <xmlpoke2 file="${file}" xpath="/" create="true">
> <data>
> <![CDATA[
> <root>
> <person name="mary" age="43">
> <child name="penelope" age="12"/>
> </person>
> </root>
> ]]>
> </data>
> </xmlpoke2>
>
> You can embed properties within the CDATA element as well, so
> you can do this:
>
> <property name="penelopysage" value="12"/>
>
> <xmlpoke2 file="${file}" xpath="/" create="true">
> <data>
> <![CDATA[
> <root>
> <person name="mary" age="43">
> <child name="penelope" age="${penelopysage}"/>
> </person>
> </root>
> ]]>
> </data>
> </xmlpoke2>
> or this:
> <xmlpoke2 file="${file}" xpath="/root" create="true">
> <data>
> <![CDATA[
> ${xml::get-xml(file, '/root')}
> <person name="mary" age="43">
> <child name="penelope" age="12"/>
> </person>
> ]]>
> </data>
> </xmlpoke2>
>
> ...which would append data to the end of your document.
>
> Neither of these is perfect at the moment, but I wanted to
> see if anyone thought they'd be useful. The <xmlpoke2> task
> could maybe do with namespace support (don't know much about
> that) and I reckon it's just begging to be merged into the
> main <xmlpoke> task. If people think they could be useful,
> then I might do some more work on them, or if you want to
> look at the source or try them out yourself just email me.
>
> Cheers
>
> John
>
>
> -------------------------------------------------------
> This SF.net email is sponsored by: Splunk Inc. Do you grep
> through log files for problems? Stop! Download the new AJAX
> search engine that makes searching your log files as easy as
> surfing the web. DOWNLOAD SPLUNK!
> http://sel.as-us.falkag.net/sel?cmd=k&kid3432&bid#0486&dat1642
> _______________________________________________
> nant-developers mailing list
> nant-developers@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/nant-developers
>