Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

From JDOM to XmlDocument
by Niel Bornstein | Pages: 1, 2

Ready, Set, Port

Remembering our basic C# language lessons from part one, let's skip right on ahead to the API-specific code.

There are a few changes we have to worry about that were not relevant in our first exercise. For example, while there is a File class in C#, it's not directly comparable to the Java File. For example, nearly all its methods are static.

As I mentioned earlier, we're converting our code not just from Java to C#, but from JDOM to DOM. While this is not necessarily a formidable task, it does complicate things a bit. Perhaps the easiest part of this port will be changing instances of JDOM's Document and Element to C#'s XmlDocument and XmlElement, respectively.

JDOM's way of doing things is often the reverse of DOM's way. For example, in our Java createDocument() method, we instantiate a root Element and then instantiate the Document, passing in the Element. In C#, we instantiate the XmlDocument, call its CreateElement() method to create an XmlElement, and then insert the resultant XmlElement as the root element of the tree with AppendChild().

A similar pattern is used in the AddCD() method; the XmlDocument's CreateElement() method is called, and the XmlElement is then inserted as a child of the root element with AppendChild(). In short, the XmlDocument serves a dual role as a representation of the document itself and as a factory to create new elements.

Another difference between JDOM and System.Xml, and indeed between JDOM and DOM itself, is that while JDOM deals exclusively with standard Java collections (such as List, in deleteCD()), DOM defines its own collections of nodes. In our C# DeleteCD() method, we're dealing with an XmlNodeList. In practical terms, an XmlNodeList is not dealt with any differently than a Java list.

Down Another Path

Upon further thought, it seems like our original Java program is missing something; there's no way to search for entries. This sounds like a job for XPath. XPathNavigator implements XPath in .NET, and you create an XPathNavigator by calling XmlNode.CreateNavigator(). The following XPath will find the Zoe Mulford CDs in my collection:


//CD[@artist='Zoe Mulford']

In C# code, we'll compile this path and select those nodes that match it, then print them.


XPathNavigator nav = document.CreateNavigator();
XPathNodeIterator iterator = 
    nav.Select("//CD[@artist=normalize-space('" + 
    artist + "')]");
Console.WriteLine("CDs by {0}", artist);
while (iterator.MoveNext()){
    XPathNavigator nav2 = iterator.Current.Clone();
    Console.WriteLine(" \"{0}\"",
        ((IHasXmlNode)nav2).GetNode().Attributes[0].Value);
}

You'll notice that I added a call to normalize-space() in the XPath. If you're not familiar with it, normalize-space() strips off leading and trailing white space from the value and reduces any repeating whitespace to a single space character. While it's not strictly necessary, I thought it might be useful in this case because the data, which was entered manually by a person, might not be normalized.

By using XPath, we can navigate directly to any CDs we want to find. This may or may not be any more efficient than searching through an entire CD catalog by hand; but it is easier to use in a consistent manner and has the added benefit that any performance improvements in the .NET runtime will automatically be reflected in your application.

So, here's our final code listing.


using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

public class CDCatalog {

    FileStream file = null;
    XmlDocument document = null;

    public static void Main(string [] args) {
        if (args.Length > 0) {
            string xmlFile = args[0];
            CDCatalog catalog = new CDCatalog(xmlFile);

            string action = args[1];

            if (args.Length == 5) {
                string title = args[2];
                string artist = args[3];
                string label = args[4];
                if (action == "add") {
                    catalog.AddCD(title, artist, 
                        label);
                } else if (action == "delete") {
                    catalog.DeleteCD(title, 
                        artist, label);
                }
            } else if (args.Length == 3) {
                string artist = args[2];
                if (action == "find") {
                    catalog.SearchForArtist(artist);
                }
            }

            // save the changed catalog
            catalog.Save();
        }
    }

    public CDCatalog(string fileName) {
        if (File.Exists(fileName)) {
            LoadDocument(fileName);
        } else {
            CreateDocument(fileName);
        }
    }

    private void LoadDocument(string fileName) {
        file = File.Open(fileName,FileMode.Open);
        document = new XmlDocument();
        document.Load(file);
    }

    private void CreateDocument(string fileName) {
        file = File.Create(fileName);
        document = new XmlDocument();
        XmlElement root = document.CreateElement("CDCatalog");
        document.AppendChild(root);
    }

    public void AddCD(string title, string artist, 
        string label) {
        XmlElement cd = document.CreateElement("CD");
        cd.SetAttribute("title",title);
        cd.SetAttribute("artist",artist);
        cd.SetAttribute("label",label);

        document.DocumentElement.AppendChild(cd);
    }

    public void DeleteCD(string title, string artist,  
        string label) {
        XmlNodeList cds = document.DocumentElement.ChildNodes;
        for (int i = 0; i < cds.Count; i++) {
            XmlElement next = (XmlElement)cds[i];
            if (next.GetAttribute("title") == title &&
                next.GetAttribute("artist") == artist &&
                next.GetAttribute("label") == label) {
                document.DocumentElement.RemoveChild(next);
            }
        }
    }

    public void SearchForArtist(string artist) {
        XPathNavigator nav = document.CreateNavigator();
        XPathExpression expr = nav.Compile(
            "//CD[@artist=normalize-space('" + artist + "')]");
        XPathNodeIterator iterator = nav.Select(expr);
        Console.WriteLine("CDs by {0}:", artist);
        while (iterator.MoveNext()){
            XPathNavigator nav2 = iterator.Current.Clone();
            Console.WriteLine(" \"{0}\"",
                ((IHasXmlNode)nav2).GetNode().Attributes[0].Value);
        }
    }

    public void Save() {
        file.Position = 0;
        XmlTextWriter writer = new XmlTextWriter(
            new StreamWriter(file));
        document.WriteTo(writer);
        file.SetLength(file.Position);
        writer.Close();
    }
}

And now we'll compile and run a couple of tests:

C:\>csc /debug /r:System.Xml.dll /t:exe CDCatalog.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.


C:\>CDCatalog CDCatalog.xml add "High Strung Tall Tales" 
    "Adrian Legg" "Relativity"

C:\>CDcatalog CDCatalog.xml find "Alison Kraus +  Union Station"
CDs by Alison Kraus +  Union Station:
 "New Favorite"

Conclusions

I've shown you how to port your SAX Java code to XmlReader and your JDOM Java code to XmlDocument, with a small helping of XPath. These are the basic technologies that most developers are familiar with, and you should now be ready to apply them in your C# programming.

But the original task I set out to accomplish was to see what could be learned from Microsoft's XML APIs. In my first article, I concluded that Microsoft's one-stop-shopping is both positive and negative, depending on your point of view. However, I'm beginning to see a greater benefit to this single source of objects; the XmlNodeType that you deal with in XmlReader is exactly the same object that you deal with in DOM. This could easily have the benefit of shortening your learning cycle, as well as making your code more reusable. The Java community could certainly stand to learn something here.

In the next installment of this series, I'll take another look at the venerable RSSReader, and make it a better C# program by using XmlReader the way it was meant to be used, as a pull-parser. And I'll compare that to some pull-parsers in the Java world.


Comment on this articleComments or questions about this article? Share them in our forum.
(* You must be a
member of XML.com to use this feature.)
Comment on this Article


Titles Only Titles Only Newest First
  • problem with org.jdom package
    2005-01-19 13:07:25 Solving_problems [Reply]

    i am new to both Linux and Netbeans.
    i am using Red Hat Enterprise Linux 3 and NetBeans 4. i am getting error when i try to run my program in NetBeans like this:


    org.jdom does not exists
    javax.servlet does not exists


    where i have to put my jar(servlet.jar or jdom.jar) files? Where and how to set classpath?


    please help me out.
    thanks
    jem

  • rdb into xml
    2002-07-06 12:35:26 tawfeeq momany [Reply]

    dear ,
    any idea to transfer data base (microsoft access ) into xml ducument

  • From RDBMS to XML
    2002-04-16 04:46:31 Reshma Bhat [Reply]

    Please give information about how to take the data from any DataBase & put it to XML document.

    • From RDBMS to XML
      2008-05-22 01:05:29 Friend4u [Reply]

      hi did u got any feedback or not if u got than plz let me know also tc bye

    • How can i use netbeans with jdom
      2005-06-27 22:53:47 DharamPaaji [Reply]

      I have installed netbean&jdk1.4.1_02,
      problem is how can i configure the jdom with netBeans ID,


      I want to read xml file and store in a database SQLServer,I want a help code which create automatically the table & field names and store the corresponding values.


      Please give me any kind of help.
      Thanks In advance.
      Dharmendra Singh

  • .NET closer to dom4j than to JDOM?
    2002-04-06 04:06:18 Jan Dvorak [Reply]

    Over time, I got used to dom4j (www.dom4j.org) to do my XML work. I found the .NET approach familiar and natural. In my judgement, porting a dom4j application would present much less of a problem beyond a language transition. There's a fine comparison of various Java DOM's at http://www-106.ibm.com/developerworks/library/x-injava2/


  • XPathNavigator
    2002-04-04 06:45:21 guy lukes [Reply]

    Wouldn't it have been easier to use


    iterator.Current.GetAttribute("title", "")
    than


    XPathNavigator nav2 = iterator.Current.Clone();
    ((IHasXmlNode)nav2).GetNode().Attributes[0].Value


    or simply use the SelectNodes method of XmlNode

    • XPathNavigator
      2002-04-04 07:02:21 Niel Bornstein [Reply]

      You've asked two questions here, and I'll take them one at a time.


      First, I used that convoluted bit of code to select nodes from XPathNavigator because that's what the .NET Framework Class Library Reference said to do, in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemxmlxmlnodeclassselectnodestopic.asp.


      As for your second point, well, yes, XmlNode.SelectNodes() would have been much simpler. I am guilty of not yet having read enough of the documentation.


      Thanks for pointing it out!

      • Revised SearchForArtist() method
        2002-04-05 07:29:15 Niel Bornstein [Reply]

        After a bit more research, here is another way to write the SearchForArtist() method. You'll notice that it does not use XPathNavigator directly, however it achieves the same result as my original code and is, I think, a little clearer to read.


        public void SearchForArtist(string artist) {


        XmlNode root = document.DocumentElement;


        XmlNodeList nodeList = root.SelectNodes("//CD[@artist=normalize-space('" + artist + "')]");


        Console.WriteLine("CDs by {0}:", artist);


        foreach (XmlElement cd in nodeList) {


        Console.WriteLine(" \"{0}\"", cd.GetAttribute("title"));


        }


        }