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
>


Reply via email to