In the XmlPeek thread, I mentioned that I was thinking of an XmlPoke task that could easily create a whole xml structure in one go.  I finally got around to doing it, and here it is:

[TaskName("xmlpoke2")]
        public class XmlPoke2 :Task
        {
          protected override void ExecuteTask()
          {
            _xml = this.XmlNode.FirstChild.Value;
            XmlDocument xd = new XmlDocument();
           
            if (System.IO.File.Exists(_xmlFile))
            {
              xd.Load(_xmlFile);
            }
            else if (_create == "true")
            {
              xd.LoadXml(_xml);
            }
            else
            {
              throw new NAnt.Core.BuildException("File " + "_xmlFile " + "could not be found, and create was set to false");
            }
           
            XmlNode xn = xd.SelectSingleNode(_xPath);
           
            if (xn.NodeType != XmlNodeType.Element && xn.NodeType != XmlNodeType.Document)
            {
              throw new NAnt.Core.BuildException("XPath string must return an element");
            }
           
            xn.InnerXml = _xml;
            xd.Save(_xmlFile);
          }
         
          [TaskAttribute("file", Required=true)]
          public string XmlFileName {
              get { return _xmlFile; }
              set { _xmlFile = value; }
          }
          [TaskAttribute("create", Required=false)]
          public string CreateIfAbsent {
              get { return _create; }
              set { _create = value; }
          }
          [TaskAttribute("xpath", Required=true)]
          public string XPathQuery {
              get { return _xPath; }
              set { _xPath = value; }
          }
         
          private string _xml;
          private string _xmlFile;
          private string _xPath;
          private string _create = "true";
        }

I've tested this from within a <script> task.  Note that this is just preliminary code that I chucked together very quickly, so it's probably a bit-rough-and ready, but seems to do the job.

The usage looks something like this:

Put some xml in an existing xml file, or create a new one if it's not already there

  <xmlpoke2 file="test.xml" xpath="/">
    <![CDATA[
      <test>
          <data>hello world</data>
      </test>
    ]]>
  </xmlpoke2>

Put some xml in an existing xml file, or throw an error if it can't be found

  <xmlpoke2 file="test.xml" xpath="/" create="false">
    <![CDATA[
      <test>
          <data>hello world</data>
      </test>
    ]]>
  </xmlpoke2>

Put some XML in a file at a specific node (useful to create a fragment of an xml document if you want to leave the rest of the document alone).

  <xmlpoke2 file="test.xml" xpath="root/secondlevel">
    <![CDATA[
      <test>
          <data>hello world</data>
      </test>
    ]]>
  </xmlpoke2>

 - The value of the xmlpoke2 task XML element should be a CDATA element with your XML in it.  This is to get around NAnt's schema enforcement.
 - The file attribute should be the XML file path.
 - The xpath attribute should be the xpath of where you want the XML placed within the file.
 - The create attribute (optional) specifies whether or not to create the xml file if it's not found.  It defaults to true.

Some other things to consider:

 - It's called xmlpoke2 to avoid clashing with the existing xmlpoke task.  Whether or not this task should be merged with the existing one is a matter for debate.
 - If the XML you want to write has CDATA elements, you'll still need to use XML escaping (&lt;[CDATA[some text]]&lt;) to make this work.  This is because the parser ends the CDATA section at the first ]]> it finds.
 - Not sure if this works well for preprocessors, DOCTYPEs, and so on.
 - If you try to use the xpath attribute to drill down into the document when the nodes you specfied in there don't exist, you'll get a NullReferenceException.  I'll probably make this a better exception at some point.

Next one I need to consider then is a task to iterate xpath results, I guess.  In the meantime, see what you think of the xmlpoke2 task.

Have fun!

John

Reply via email to