The Attr Interface

Probably 90% of the time, everything you need to do with attributes can be done purely through the various attribute methods of the Element interface. However, occasionally the Attr interface comes in handy. An Attr object represents an attribute, whether explicitly included in the document or defaulted in from the schema.

Example 11.15 summarizes the Attr interface. Besides the methods inherited from the Node super-interface, Attr includes methods to get the full name of the attribute (including a prefix if any), determine whether or not the attribute was specified in the document or merely defaulted in from the DTD or schema, get the element that contains this attribute, and get the value of the attribute. Entity and character references in the value are resolved, but white space is not normalized. There’s also a method that can change the value of this attribute. This throws a DOMException if the attribute is read-only, as it might be if this attribute belongs to an element in a read-only entity reference node.

Example 11.15. The Attr interface

package org.w3c.dom;

public interface Attr extends Node {

  public String  getName();  // Prefixed name
  public String  getValue(); 
  public Element getOwnerElement();
  public boolean getSpecified();
  public void    setValue(String value) throws DOMException;

}

The key thing to watch out for when working with attributes in DOM is that they are not considered to be children of the elements that contain them. Furthermore, unlike the XPath data model, the elements that contain them are not their parents either. In DOM attributes have no parents, so invoking getParent() on an Attr always returns null.

In DOM terminology an attribute that is actually placed on the start-tag of an element is called specified. An attribute that is defaulted in from the DTD or schema is not specified. You can tell which is which by invoking the getSpecified() method. This is one of the few things you can do with an Attr object that you can’t do with the owning Element object.

One source of interoperability problems in XML is that some parsers validate and some don’t. If validation were only about checking constraints, this wouldn’t be a big problem. Unfortunately validation does more than that. In particular it assigns default attribute values to elements. These attributes may be seen by a validating parser but not by a non-validating parser. Therefore, for an example of this interface, I’m going to write a program that converts unspecified attributes into specified attributes to improve interoperability. An attribute modified or added programmatically in DOM is always specified, even if it has the same value as the default attribute in the DTD. If you delete an attribute for which the DTD provides a default value, then DOM will insert an attribute node with the default value. Thus it’s impossible to get rid of a default attribute. Example 11.16 demonstrates.[4]

Example 11.16. Specifying all attributes

import org.w3c.dom.*;


public class AttributeUtility {

  // Recursively descend the tree replacing all unspecified
  // attributes with specified attributes
  public static void specifyAttributes(Node node) {
    
    int type = node.getNodeType();
    if (type == Node.ELEMENT_NODE) { 
                // the only type with attributes
      Element element = (Element) node;
      NamedNodeMap attributes = element.getAttributes();
      Document factory = node.getOwnerDocument();
      for (int i = 0; i < attributes.getLength(); i++) {
        Attr attribute = (Attr) attributes.item(i);
        if (!attribute.getSpecified()) {
          // We can't change the specified property of an
          // attribute in DOM2. However, attributes are specified
          // by default, so if we delete the old attribute and 
          // add a new one with the same name and value, or 
          // change the attribute's value (even  to the same 
          // thing) the effect is what we're looking for.
          String name = attribute.getName();
          String value = attribute.getValue();
          Attr specifiedAttribute = factory.createAttribute(name);
          specifiedAttribute.setValue(value);
          element.setAttributeNode(specifiedAttribute);
          // This replaces the old attribute with the same name.
        }
      } // end for   
    } // end if 
    
    if (node.hasChildNodes()) {
      NodeList children = node.getChildNodes();
      for (int i = 0; i < children.getLength(); i++) {
        Node child = children.item(i);
        specifyAttributes(child);
      } // end for
    } // end if 
    
  }  // end specifyAttributes()
  
}

This program simply recursively descends the tree beneath a node, looking for elements. It scans each element’s attributes. If any of those attributes are not specified, then the attribute is deleted and replaced with a copy.

DOM2 is missing one crucial feature for working with attributes. It cannot tell you the type of the attribute as declared in the DTD. That is, you cannot find out if an attribute is CDATA, ID, IDREF, NMTOKEN, and so forth.



[4] I admit this example is a little contrived. Reading each value and writing the same value back out again would be easier.


Copyright 2001, 2002 Elliotte Rusty Haroldelharo@metalab.unc.eduLast Modified July 12, 2002
Up To Cafe con Leche