一个自写的XML读写/存取属性的Java工具类库

- 中国WEB开发者网络 (http://www.webasp.net)
-- 技术教程 (http://www.webasp.net/article/)
--- 一个自写的XML读写/存取属性的Java工具类库 (http://www.webasp.net/article/27/26576.htm)
-- 作者:未知
-- 发布日期: 2005-09-19
 
Java 5中的Properties类现在可以使用XML存取,通过loadFromXML和storeToXML方法实现。假设有下面这个属性表:
windowSize:  400,400
windowLocation:  456,300
 
使用storeToXML后会得到这样的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Comment</comment>
<entry key="windowLocation">400,400</entry>
<entry key="windowSize">456,300</entry>
</properties>
但是如果要获得更具层次感的属性文件,可以使用这里我写的一个Utility。它建立在一个读取和存储XML的类库上。这个类库采集于Columba Project的util包,并有所修改。
 
首先是XmlElement,用于表示XML文件里的一个entry
 
/*
 * @(#)XmlElement.java
 * Created on 2005-8-12
 */
package com.allenstudio.ir.util;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Vector;
/**
* The XmlElement is a generic containment class for elements within an XML
* file.
* <p>
*
* It extends Observable which should be used for gui elements which are
* interested in configuration changes.
* <p>
*
* Show interested in:
*
* <pre>
* xmlElement.addObserver(yourObserver);
* </pre>
*
* <p>
* When making bigger changes on XmlElement and probably its subnodes and/or a
* greater number of attributes at once, you should just change XmlElement
* directly and manually notify the Observers by calling:
* <p>
*
* <pre>
* xmlElement.setChanged();
* xmlElement.notifyObservers();
* </pre>
*
* <p>
* There a good introduction for the Observable/Observer pattern in
* Model/View/Controller based applications at www.javaworld.com: -
* {@link http://www.javaworld.com/javaworld/jw-10-1996/jw-10-howto.html}
*
* @author fdietz
*/
public class XmlElement extends Observable implements Cloneable {
    String name;
    String data;
    Hashtable<String, String> attributes;
    List<XmlElement> subElements;
    XmlElement parent;
    /**
     *
     *
     * Constructor
     * 
     */
    public XmlElement() {
        subElements = new Vector<XmlElement>();
        this.attributes = new Hashtable<String, String>(10);
    }
    /**
     * **
     *
     * Constructor
     *
     * @param String
     *            Name
     * 
     */
    public XmlElement(String name) {
        this.name = name;
        this.attributes = new Hashtable<String, String>(10);
        subElements = new Vector<XmlElement>();
        data = "";
    }
    /**
     * **
     *
     * Constructor
     *
     * @param String
     *            Name
     * @param Hashtable
     *            Attributes
     * 
     */
    public XmlElement(String name, Hashtable<String, String> attributes) {
        this.name = name;
        this.attributes = attributes;
        subElements = new Vector<XmlElement>();
    }
    /**
     * **
     *
     * Constructor
     *
     * @param Name
     *            String
     * @param Data
     *            String
     * 
     */
    public XmlElement(String name, String data) {
        this.name = name;
        this.data = data;
        subElements = new Vector<XmlElement>();
        this.attributes = new Hashtable<String, String>(10);
    }
    /**
     * Add attribute to this xml element.
     *
     * @param name
     *            name of key
     * @param value
     *            new attribute value
     * @return old attribute value
     * 
     */
    public Object addAttribute(String name, String value) {
        if ((value != null) && (name != null)) {
            Object returnValue = attributes.put(name, value);
            return returnValue;
        }
        return null;
    }
    /**
     * **
     *
     * @return String
     * @param String
     *            Name
     * 
     */
    public String getAttribute(String name) {
        return ((String) attributes.get(name));
    }
    public String getAttribute(String name, String defaultValue) {
        if (getAttribute(name) == null) {
            addAttribute(name, defaultValue);
        }
        return getAttribute(name);
    }
    /**
     * **
     *
     * @return String
     * @param String
     *            Name
     * 
     */
    public Hashtable<String, String> getAttributes() {
        return attributes;
    }
    /**
     * **
     *
     *
     * @param Attrs
     *            Hashtable to use as the attributes
     * 
     */
    public void setAttributes(Hashtable<String, String> attrs) {
        attributes = attrs;
    }
    /**
     * **
     *
     * @return Enumeration
     * 
     */
    public Enumeration getAttributeNames() {
        return (attributes.keys());
    }
    /**
     * **
     *
     * @return boolean
     * @param XmlElement
     *            E
     * 
     */
    public boolean addElement(XmlElement e) {
        e.setParent(this);
        return (subElements.add(e));
    }
    public XmlElement removeElement(XmlElement e) {
        XmlElement child = null;
        for (int i = 0; i < subElements.size(); i++) {
            child = (XmlElement) subElements.get(i);
            // FIXME -- This will most likely not work.
            //          You want the element removed if the contents are the same
            //          Not just if the element reference is the same.
            if (child == e) {
                subElements.remove(i);
            }
        }
        return (child);
    }
    public XmlElement removeElement(int index) {
        return (XmlElement) subElements.remove(index);
    }
    public void removeAllElements() {
        subElements.clear();
    }
    /**
     * convienience method for the TreeView
     *
     * this method is modeled after the DefaultMutableTreeNode-class
     *
     * DefaultMutableTreeNode wraps XmlElement for this purpose
     * 
     */
    public void removeFromParent() {
        if (parent == null) {
            return;
        }
        parent.removeElement(this);
        parent = null;
    }
    public void append(XmlElement e) {
        e.removeFromParent();
        addElement(e);
    }
    /**
     *
     * convienience method for the TreeView
     *
     * @param e
     * @param index
     */
    public void insertElement(XmlElement e, int index) {
        e.removeFromParent();
        subElements.add(index, e);
        e.setParent(this);
    }
    /**
     * **
     *
     * @return Vector
     * 
     */
    public List getElements() {
        return subElements;
    }
    public int count() {
        return subElements.size();
    }
    /**
     * Returns the element whose hierachy is indicated
     * by <code>path</code>. The path is separated with
     * periods(".").<br>
     * <em>Note: if one node has more than one elements
     * that have the same name, that is, if its subnodes
     * have the same path, only the first one is returned.
     * </em>
     * @return the first element qualified with the path
     * @param path the path string of the specified element
     */
    public XmlElement getElement(String path) {
        int i = path.indexOf('.');
        String topName;
        String subName;
        if (i == 0) {
            path = path.substring(1);
            i = path.indexOf('.');
        }
        if (i > 0) {
            topName = path.substring(0, i);
            subName = path.substring(i + 1);
        } else {
            topName = path;
            subName = null;
        }
        int j;
        for (j = 0; j < subElements.size(); j++) {
            if (((XmlElement) subElements.get(j)).getName().equals(topName)) {
                if (subName != null) {
                    return (((XmlElement) subElements.get(j))
                            .getElement(subName));
                } else {
                    return ((XmlElement) subElements.get(j));
                }
            }
        }
        return null;
    }
    public XmlElement getElement(int index) {
        return (XmlElement) subElements.get(index);
    }
    /**
     * Adds a sub element to this one. The path
     * is separated with dots(".").
     *
     * @return the <code>XmlElement</code> added
     * @param path The subpath of the sub element to add
     * 
     */
    public XmlElement addSubElement(String path) {
        XmlElement parent = this;
        XmlElement child;
        String name;
        while (path.indexOf('.') != -1) {
            name = path.substring(0, path.indexOf('.'));
            path = path.substring(path.indexOf('.') + 1);
            // if path startsWith "/" -> skip
            if (name.length() == 0)
                continue;
            if (parent.getElement(name) != null) {
                parent = parent.getElement(name);
            } else {
                child = new XmlElement(name);
                parent.addElement(child);
                parent = child;
            }
        }
        child = new XmlElement(path);
        parent.addElement(child);
        return child;
    }
    /**
     * Adds a sub element to this one
     *
     * @return XmlElement
     * @param element
     *            The XmlElement to add
     * 
     */
    public XmlElement addSubElement(XmlElement e) {
        e.setParent(this);
        subElements.add(e);
        return e;
    }
    /**
     * Adds a sub element to this one
     *
     * @return XmlElement
     * @param Name
     *            The name of the sub element to add
     * @param Data
     *            String Data for this element
     */
    public XmlElement addSubElement(String name, String data) {
        XmlElement e = new XmlElement(name);
        e.setData(data);
        e.setParent(this);
        subElements.add(e);
        return e;
    }
    /**
     * Sets the parent element
     *
     * @param Parent
     *            The XmlElement that contains this one
     * 
     */
    public void setParent(XmlElement parent) {
        this.parent = parent;
    }
    /**
     * Gives the XmlElement containing the current element
     *
     * @return XmlElement
     * 
     */
    public XmlElement getParent() {
        return parent;
    }
    /**
     * Sets the data for this element
     *
     * @param D
     *            The String representation of the data
     * 
     */
    public void setData(String d) {
        data = d;
    }
    /**
     * Returns the data associated with the current Xml element
     *
     * @return String
     * 
     */
    public String getData() {
        return data;
    }
    /**
     * Returns the name of the current Xml element
     *
     * @return String
     * 
     */
    public String getName() {
        return name;
    }
    /**
     * **
     *
     * @param out
     *            OutputStream to print the data to
     * 
     */
    /*
     * public void write(OutputStream out) throws IOException { PrintWriter PW =
     * new PrintWriter(out); PW.println(" <?xml version=\"1.0\"
     * encoding=\"UTF-8\"?>"); if (SubElements.size() > 0) { for (int i = 0; i <
     * SubElements.size(); i++) { ((XmlElement)
     * SubElements.get(i))._writeSubNode(PW, 4); } } PW.flush(); }
     */
    /**
     * Prints sub nodes to the given data stream
     *
     * @param out
     *            PrintWriter to use for printing
     * @param indent
     *            Number of spaces to indent things
     * 
     */
    /*
     * private void _writeSubNode(PrintWriter out, int indent) throws
     * IOException { _writeSpace(out, indent); out.print(" <" + Name); //if (
     * Attributes.size()>1) out.print(" ");
     *
     * for (Enumeration e = Attributes.keys(); e.hasMoreElements();) { String K =
     * (String) e.nextElement(); out.print(K + "=\"" + Attributes.get(K) + "\"
     * b");
     *  } out.print(">");
     *
     * if (Data != null && !Data.equals("")) { if (Data.length() > 20) {
     * out.println(""); _writeSpace(out, indent + 2); } out.print(Data); } if
     * (SubElements.size() > 0) { out.println(""); for (int i = 0; i <
     * SubElements.size(); i++) { ((XmlElement)
     * SubElements.get(i))._writeSubNode( out, indent + 4); } _writeSpace(out,
     * indent); } out.println(" </" + Name + ">");
     *  }
     */
    /**
     * Prints out a given number of spaces
     *
     * @param out
     *            PrintWriter to use for printing
     * @param numSpaces
     *            Number of spaces to print
     * 
     */
    /*
     * private void _writeSpace(PrintWriter out, int numSpaces) throws
     * IOException {
     *
     * for (int i = 0; i < numSpaces; i++) out.print(" "); }
     *
     * public static void printNode(XmlElement Node, String indent) { String
     * Data = Node.getData(); if (Data == null || Data.equals("")) {
     * System.out.println(indent + Node.getName()); } else {
     * System.out.println(indent + Node.getName() + " = '" + Data + "'"); }
     * Vector Subs = Node.getElements(); int i, j; for (i = 0; i < Subs.size();
     * i++) { printNode((XmlElement) Subs.get(i), indent + " "); } }
     */
    public static void printNode(XmlElement node, String indent) {
        String data = node.getData();
        if ((data == null) || data.equals("")) {
            System.out.println(indent + node.getName());
        } else {
            System.out.println(indent + node.getName() + " = '" + data + "'");
        }
        // print attributes
        for (Enumeration enumeration = node.getAttributes().keys(); enumeration
                .hasMoreElements();) {
            String key = (String) enumeration.nextElement();
            String value = node.getAttribute(key);
            System.out.println(indent + key + ":" + value);
        }
        List subs = node.getElements();
        for (Iterator it = subs.iterator(); it.hasNext();) {
            printNode((XmlElement) it.next(), indent + "    ");
            // for (i = 0; i < subs.size(); i++) {
            // printNode((XmlElement) subs.get(i), indent + " ");
        }
    }
    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    @Override
    public Object clone() {
        try {
            XmlElement clone = (XmlElement) super.clone(); // creates a shallow
                                                           // copy of this
                                                           // object
            if (attributes != null) {
                clone.setAttributes((Hashtable<String, String>) getAttributes().clone());
            }
            if (subElements != null) {
                clone.subElements = new Vector();
                List childs = getElements();
                XmlElement child;
                for (Iterator it = childs.iterator(); it.hasNext();) {
                    child = (XmlElement) it.next();
                    // for( int i=0; i<childs.size(); i++ ) {
                    // child = (XmlElement) childs.get(i);
                    clone.addSubElement((XmlElement) child.clone());
                }
            }
            return clone;
        } catch (CloneNotSupportedException cnse) {
            throw new InternalError("Could not clone XmlElement: " + cnse);
        }
    }
    /**
     * Sets the name.
     *
     * @param name
     *            The name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * Notify all Observers.
     *
     * @see java.util.Observable#notifyObservers()
     */
    @Override
    public void notifyObservers() {
        setChanged();
        super.notifyObservers();
    }
    /**
     * Returns true if the specified objects are equal. They are equal if they
     * are both null OR if the <code>equals()</code> method return true. (
     * <code>obj1.equals(obj2)</code>).
     *
     * @param obj1
     *            first object to compare with.
     * @param obj2
     *            second object to compare with.
     * @return true if they represent the same object; false if one of them is
     *         null or the <code>equals()</code> method returns false.
     */
    private boolean equals(Object obj1, Object obj2) {
        boolean equal = false;
        if ((obj1 == null) && (obj2 == null)) {
            equal = true;
        } else if ((obj1 != null) && (obj2 != null)) {
            equal = obj1.equals(obj2);
        }
        return equal;
    }
    /** {@inheritDoc}
     *Recursive comparison.
     */
    @Override
    public boolean equals(Object obj) {
        boolean equal = false;
        if ((obj != null) && (obj instanceof XmlElement)) {
            XmlElement other = (XmlElement) obj;
            if (equals(attributes, other.attributes)
                    && equals(data, other.data) && equals(name, other.name)
                    && equals(subElements, other.subElements)) {
                equal = true;
            }
        }
        return equal;
    }
    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        //Hashcode value should be buffered.
        int hashCode = 23;
        if (attributes != null) {
            hashCode += (attributes.hashCode() * 13);
        }
        if (data != null) {
            hashCode += (data.hashCode() * 17);
        }
        if (name != null) {
            hashCode += (name.hashCode() * 29);
        }
        if (subElements != null) {
            hashCode += (subElements.hashCode() * 57);
        }
        return hashCode;
    }
}
然后是XmlIO,用于读写。
 
/*
 * @(#)XmlIO.java
 * Created on 2005-8-12
 */
package com.allenstudio.ir.util;
import java.io.BufferedWriter;
import java.io.CharArrayWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
 * XML IO reading and writing utility.
 *
 * @author fdietz
 */
public class XmlIO extends DefaultHandler {
  private static final Logger LOG = Logger.getLogger("org.columba.core.xml");
  private static final String ROOT_XML_ELEMENT_NAME = "__INSPIRENTO_XML_TREE_TOP__";
  // List of sub-elements
  @SuppressWarnings("unused")
  private List<XmlElement> elements;
  // Top level element (Used to hold everything else)
  private XmlElement rootElement;
  // The current element you are working on
  private XmlElement currentElement;
  // For writing out the data
  // Indent for each level
  private int writeIndent = 2;
  // Maximum data to put on a "one liner"
  private int maxOneLineData = 20;
  // The SAX 2 parser...
  @SuppressWarnings("unused")
private XMLReader xr;
  // Buffer for collecting data from
  // the "characters" SAX event.
  private CharArrayWriter contents = new CharArrayWriter();
  private URL url = null;
  /*
  // Default constructor
  public XmlIO() {
  }
  */
  /*
  // setup and load constructor
  public XmlIO(String FilePath) {
          currentElement = null;

  }
  */
  public XmlIO(URL url) {
      super();
      this.url = url;
  }
  // setup and load constructor
  public XmlIO() {
      currentElement = null;
  }
  // setup and load constructor
  /**
   * Creates a XmlIO object with the specified element at the top.
   * @param element the element at the top.
   */
  public XmlIO(XmlElement element) {
      rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
      rootElement.addElement(element);
  }
  public void setURL(URL url) {
      this.url = url;
  }
  public boolean load() {
      //this.file = F;
      return load(url);
  }
  // Load a file. This is what starts things off.
  /**
   * Loads from the InputStream into the root Xml Element.
   * @param input the input stream to load from.
   */
  public boolean load(InputStream input) {
      elements = new Vector<XmlElement>();
      rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
      currentElement = rootElement;
      try {
          // Create the XML reader...
          //      xr = XMLReaderFactory.createXMLReader();
          SAXParserFactory factory = SAXParserFactory.newInstance();
          // Set the ContentHandler...
          //      xr.setContentHandler( this );
          SAXParser saxParser = factory.newSAXParser();
          saxParser.parse(input, this);
      } catch (javax.xml.parsers.ParserConfigurationException ex) {
          LOG.severe("XML config error while attempting to read from the input stream \n'" + input + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      } catch (SAXException ex) {
          // Error
          LOG.severe("XML parse error while attempting to read from the input stream \n'" + input + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      } catch (IOException ex) {
          LOG.severe("I/O error while attempting to read from the input stream \n'" + input + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      }
      //XmlElement.printNode( getRoot(), "");
      return (true);
  }
  /**
   * Load a file. This is what starts things off.
   * @param inputURL the URL to load XML from.
   */
  public boolean load(URL inputURL) {
      elements = new Vector<XmlElement>();
      rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
      currentElement = rootElement;
      try {
          // Create the XML reader...
          //      xr = XMLReaderFactory.createXMLReader();
          SAXParserFactory factory = SAXParserFactory.newInstance();
          // Set the ContentHandler...
          //      xr.setContentHandler( this );
          SAXParser saxParser = factory.newSAXParser();
          saxParser.parse(inputURL.toString(), this);
      } catch (javax.xml.parsers.ParserConfigurationException ex) {
          LOG.severe("XML config error while attempting to read XML file \n'" + inputURL + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      } catch (SAXException ex) {
          // Error
          LOG.severe("XML parse error while attempting to read XML file \n'" + inputURL + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      } catch (IOException ex) {
          LOG.severe("I/O error while attempting to read XML file \n'" + inputURL + "'");
          LOG.severe(ex.toString());
          ex.printStackTrace();
          return (false);
      }
      //XmlElement.printNode( getRoot(), "");
      return (true);
  }
  // Implement the content hander methods that
  // will delegate SAX events to the tag tracker network.
  @Override
public void startElement(String namespaceURI, String localName,
      String qName, Attributes attrs) throws SAXException {
      // Resetting contents buffer.
      // Assuming that tags either tag content or children, not both.
      // This is usually the case with XML that is representing
      // data strucutures in a programming language independant way.
      // This assumption is not typically valid where XML is being
      // used in the classical text mark up style where tagging
      // is used to style content and several styles may overlap
      // at once.
      try {
          contents.reset();
          String name = localName; // element name
          if (name.equals("")) {
              name = qName; // namespaceAware = false
          }
          XmlElement p = currentElement;
          currentElement = currentElement.addSubElement(name);
          currentElement.setParent(p);
          if (attrs != null) {
              for (int i = 0; i < attrs.getLength(); i++) {
                  String aName = attrs.getLocalName(i); // Attr name
                  if (aName.equals("")) {
                      aName = attrs.getQName(i);
                  }
                  currentElement.addAttribute(aName, attrs.getValue(i));
              }
          }
      } catch (java.lang.NullPointerException ex) {
          LOG.severe("Null!!!");
          LOG.severe(ex.toString());
          ex.printStackTrace();
      }
  }
  @Override
public void endElement(String namespaceURI, String localName, String qName)
      throws SAXException {
      currentElement.setData(contents.toString().trim());
      contents.reset();
      currentElement = currentElement.getParent();
  }
  @Override
public void characters(char[] ch, int start, int length)
      throws SAXException {
      // accumulate the contents into a buffer.
      contents.write(ch, start, length);
  }
  /**
   * Returns the root for the XmlElement hiearchy.
   * Note that this Xml Element will always have the name <code>__COLUMBA_XML_TREE_TOP__</code>.
   * <p>
   * Methods that want to retrieve elements from this root should use
   * the {@link XmlElement#getElement(String)} in order to get the wanted
   * element.
   * @return a XmlElement if it has been loaded or initialized with it; null otherwise.
   */
  public XmlElement getRoot() {
      return (rootElement);
  }
  public void errorDialog(String Msg) {
      JOptionPane.showMessageDialog(null, "Error: " + Msg);
  }
  public void warningDialog(String Msg) {
      JOptionPane.showMessageDialog(null, "Warning: " + Msg);
  }
  public void infoDialog(String Msg) {
      JOptionPane.showMessageDialog(null, "Info: " + Msg);
  }
  public void save() throws Exception {
      write(new FileOutputStream(url.getPath()));
  }
  //
  // Writer interface
  //
  public void write(OutputStream out) throws IOException {
      BufferedWriter PW = new BufferedWriter(new OutputStreamWriter(out,
                  "UTF-8"));
      PW.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
      if (rootElement.subElements.size() > 0) {
          for (int i = 0; i < rootElement.subElements.size(); i++) {
              _writeSubNode(PW, (XmlElement) rootElement.subElements.get(i), 0);
          }
      }
      PW.flush();
  }
  private void _writeSubNode(Writer out, XmlElement element, int indent)
      throws IOException {
      _writeSpace(out, indent);
      out.write("<");
      out.write(element.getName());
      for (Enumeration e = element.getAttributeNames(); e.hasMoreElements();) {
          String K = (String) e.nextElement();
          out.write(" " + K + "=\"" + InspirentoUtilities.escapeText(element.getAttribute(K)) + "\"");
      }
      out.write(">");
      String data = element.getData();
      if ((data != null) && !data.equals("")) {
          if (data.length() > maxOneLineData) {
              out.write("\n");
              _writeSpace(out, indent + writeIndent);
          }
          out.write(InspirentoUtilities.escapeText(data));
      }
      List subElements = element.getElements();
      if (subElements.size() > 0) {
          out.write("\n");
          for (Iterator it = subElements.iterator(); it.hasNext();) {
              _writeSubNode(out, (XmlElement) it.next(), indent + writeIndent);
              // for (int i = 0; i < subElements.size(); i++) {
              // _writeSubNode(
              // out,
              // (XmlElement) subElements.get(i),
              // indent + writeIndent);
          }
          _writeSpace(out, indent);
      }
      if (data.length() > maxOneLineData) {
          out.write("\n");
          _writeSpace(out, indent);
      }
      out.write("</" + InspirentoUtilities.escapeText(element.getName()) + ">\n");
  }
  private void _writeSpace(Writer out, int numSpaces)
      throws IOException {
      for (int i = 0; i < numSpaces; i++) {
          out.write(" ");
      }
  }
}
 
 
下面是继承Properties的ConfigurationManager,其中的getProperty和setProperty方法已经被Overridden。
 
/*
 * @(#)ConfigurationManager.java
 * Created on 2005-8-10
 */
package com.allenstudio.ir.core;
import java.util.*;
import java.io.*;
import com.allenstudio.ir.util.*;
/**
 * Manages the configuration for Inspirento.<br>
 * This manager uses XML format to store information.
 * The configuration file is, by default, saved in the
 * "config" directory and named "config.xml". Clearly,
 * this class should be a singleton, so we use
 * {@link #getInstance()} to get an instance and call
 * other instance methods to get the settings needed
 * by Inspirento, such as "windowSize", "windowLocation",
 * and etc.<br>
 * The program first tries to get the configuration from
 * this <code>ConfigurationManager</code>. If it fails to
 * get any key, it uses the default settings presetted in
 * the protected <code>default</code> field.
 *
 * @author Allen Chue
 */
public class ConfigurationManager extends Properties {
   
    public static final String CONFIG_DIRECTORY = "config";
   
    public static final String CONFIG_FILE = "config.xml";
   
    public static final String COMMON_PREFIX = "Inspirento.";
   
    private static ConfigurationManager instance = null;
   
    private XmlIO xmlIO;
       
    /**
     * Private constructor for singleton use.
     */
    private ConfigurationManager() {
        initDefaultSettings();
       
        readIn();
    }
   
    public static ConfigurationManager getInstance() {
        if (instance != null) {
            return instance;
        } else {
            instance = new ConfigurationManager();
            return instance;
        }
    }
   
    public void readIn() {
        try {
            File configFile = new File(
                    CONFIG_DIRECTORY +
                    System.getProperty("file.separator") +
                    CONFIG_FILE);//$NON-NLS-1$
            if (configFile.exists()) {
                FileInputStream configStream = new FileInputStream(configFile);
                xmlIO = new XmlIO();
                xmlIO.load(configStream);
                configStream.close();
            }
        } catch (Exception e) {
            System.out.println("Cannot load configuration file" +
                    " supposed to be at \"config\\config.xml\"" +
                    "\nDefault settings will be stored as the replacement.");//$NON-NLS-1$
            writeDefaultsToFile();
            e.printStackTrace();
        }
    }
   
    public void writeBack() {
        try {
            FileOutputStream configFile = new FileOutputStream(
                    CONFIG_DIRECTORY +
                    System.getProperty("file.separator") +
                    CONFIG_FILE);
            xmlIO.write(configFile);
            configFile.close();
        } catch (Exception e) {
            System.out.println("Cannot write configuration file" +
                    " to \"config\\config.xml\"");//$NON-NLS-1$
            e.printStackTrace();
        }
    }
   
    /**
     * Uses XML parser to get the specified property.
     * If there is no such a key, the method returns
     * <code>null</code>.
     * @param key the key of the property
     * @return the property value
     */
    @Override
    public synchronized String getProperty(String key) {
        String value = xmlIO.getRoot().getElement(Constants.PROJECT_NAME +
                "." + getPath(key)[0]).getAttribute(getPath(key)[1]);
        if (value == null) {//Perhaps some element is lost in the file
            value = defaults.getProperty(key);
            setProperty(key, value);//null value has no side effect
            new Thread(){
                @Override
                public void run() {
                    writeBack();
                }
            }.start();
        }
       
        return value;
    }
   
    @Override
    public synchronized Object setProperty(String key, String value) {
        xmlIO.getRoot().getElement(Constants.PROJECT_NAME +
                "." + getPath(key)[0]).addAttribute(getPath(key)[1], value);
       
        return value;
    }
   
    /**
     * When the configuration file is lost, this method
     * is used to write the default settings stored in
     * the program itself to file.
     *
     */
    private void writeDefaultsToFile() {
        Enumeration keys = defaults.keys();
       
        XmlElement xe = new XmlElement(Constants.PROJECT_NAME);
        xmlIO = new XmlIO(xe);
        for (; keys.hasMoreElements(); ) {
            String pathText = (String)keys.nextElement();
            String[] path = getPath(pathText);
            //Test if the element to be modified exists
            XmlElement elementAdded = xe.getElement(path[0]);
            if (elementAdded == null){
                elementAdded = xe.addSubElement(path[0]);
            }
            elementAdded.addAttribute(path[1], defaults.getProperty(pathText));
           
        }
       
        try {
            FileOutputStream configFile = new FileOutputStream(
                    CONFIG_DIRECTORY +
                    System.getProperty("file.separator") +
                    CONFIG_FILE);//$NON-NLS-1$
            xmlIO.write(configFile);
            configFile.close();
        } catch (Exception e) {
            System.out.println("Cannot write configuration file" +
                    " to \"config\\config.xml\"");//$NON-NLS-1$
            e.printStackTrace();
        }
    }
   
    /**
     * Returns an string array of length 2.
     * The parameter <code>pathText</code> is supposed to
     * be a string separated with dots. For example,
     * "Inspirento.window.location" is a valid parameter.
     * This method puts the token after the last dot in
     * the second position of the result array, and the
     * remaining string(excluding the last dot) in the first
     * position of the result array. It is a rivate helping method.
     * <br>
     * Example: getPath("Inspirento.window.location") returns
     * the array {"Inspirento.window", "location"}.<br>
     * <em>No format checking is done in this method! <code>
     * ArrayOutOfBoundsException</code> will be thrown
     * when no dots are found in the string.</em>
     * @param pathText the path text to be processed
     * @return an array containing the result
     */
    private static String[] getPath(String pathText) {
        int dotPos = pathText.lastIndexOf('.');
       
        String[] result = new String[2];
        result[0] = pathText.substring(0, dotPos);
        result[1] = pathText.substring(dotPos + 1);
       
        return result;
    }
   
    private void initDefaultSettings() {
        String[] configDefaults = {
                "window.location", "400,300",
                "window.size", "450,300"
        };
        defaults = new Properties();
        for(int i = 0, max = configDefaults.length; i < max; i += 2) {
            String value = configDefaults[i + 1];
            defaults.setProperty(configDefaults[i], value);
        }
    }
}

在上面的代码中,Inspirento是我的项目,可以根据情况修改。实际这个类是使用在整个程序的配置获取和修改的。如果用它处理前面的属性,可以得到这样的文件,可以看到它有层次感,更便于处理和阅读。
 
<?xml version="1.0" encoding="UTF-8"?>
<Inspirento>
  <window location="202 ,179" size="532 ,455"></window>
</Inspirento>

webasp.net