2013-03-25 15 views
8

Od lat analizuję XML tak, i muszę przyznać, że kiedy liczba różnych elementów staje się większa, uważam, że jest to trochę nudne i wyczerpujące, oto co mam na myśli, próbka manekin XML:Lepszy sposób na parsowanie xml

<?xml version="1.0"?> 
<Order> 
    <Date>2003/07/04</Date> 
    <CustomerId>123</CustomerId> 
    <CustomerName>Acme Alpha</CustomerName> 
    <Item> 
     <ItemId> 987</ItemId> 
     <ItemName>Coupler</ItemName> 
     <Quantity>5</Quantity> 
    </Item> 
    <Item> 
     <ItemId>654</ItemId> 
     <ItemName>Connector</ItemName> 
     <Quantity unit="12">3</Quantity> 
    </Item> 
    <Item> 
     <ItemId>579</ItemId> 
     <ItemName>Clasp</ItemName> 
     <Quantity>1</Quantity> 
    </Item> 
</Order> 

jest to istotne części (używając sax):

public class SaxParser extends DefaultHandler { 

    boolean isItem = false; 
    boolean isOrder = false; 
    boolean isDate = false; 
    boolean isCustomerId = false; 
    private Order order; 
    private Item item; 

     @Override 
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { 
     if (localName.equalsIgnoreCase("ORDER")) { 
      order = new Order(); 
     } 

     if (localName.equalsIgnoreCase("DATE")) { 
      isDate = true; 
     } 

     if (localName.equalsIgnoreCase("CUSTOMERID")) { 
      isCustomerId = true; 
     } 

     if (localName.equalsIgnoreCase("ITEM")) { 
      isItem = true; 
     } 
    } 

    public void characters(char ch[], int start, int length) throws SAXException { 

     if (isDate){ 
      SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd"); 
      String value = new String(ch, start, length); 
      try { 
       order.setDate(formatter.parse(value)); 
      } catch (ParseException e) { 
       e.printStackTrace(); 
      } 
     } 

     if(isCustomerId){ 
      order.setCustomerId(Integer.valueOf(new String(ch, start, length))); 
     } 

     if (isItem) { 
      item = new Item(); 
      isItem = false; 
     } 



    } 

} 

Zastanawiam się czy jest jakiś sposób, aby pozbyć się tych ohydnych logicznych, które stale rosnąć z wielu elementów. Musi być lepszy sposób na przeanalizowanie tego stosunkowo prostego pliku XML. Po prostu patrząc na linie kodu niezbędne do wykonania tego zadania wygląda brzydko.

Obecnie używam parser SAX, ale jestem otwarty na wszelkie inne sugestie (inne niż DOM, nie mogę sobie pozwolić na parserów pamięci mam ogromne pliki XML).

+4

Możesz wypróbować StAX –

+0

Jeśli masz koncertowy model danych, który generuje XML, powinienem rzucić okiem na XStream (http://xstream.codehaus.org/). Robi naprawdę fajną robotę serializacji danych do xml iz powrotem. –

+1

Na temat, lubię zacząć od XSD i używania XmlBeans. Nieco starsze, znaczniki XML mają rozróżniać wielkie i małe litery, a ten kod je zepsuje. –

Odpowiedz

5

Oto przykład użycia JAXB z StAX.

dokument

Wejście:

<?xml version="1.0" encoding="UTF-8"?> 
<Personlist xmlns="http://example.org"> 
    <Person> 
     <Name>Name 1</Name> 
     <Address> 
      <StreetAddress>Somestreet</StreetAddress> 
      <PostalCode>00001</PostalCode> 
      <CountryName>Finland</CountryName> 
     </Address> 
    </Person> 
    <Person> 
     <Name>Name 2</Name> 
     <Address> 
      <StreetAddress>Someotherstreet</StreetAddress> 
      <PostalCode>43400</PostalCode> 
      <CountryName>Sweden</CountryName> 
     </Address> 
    </Person> 
</Personlist> 

Person.java:

@XmlRootElement(name = "Person", namespace = "http://example.org") 
public class Person { 
    @XmlElement(name = "Name", namespace = "http://example.org") 
    private String name; 
    @XmlElement(name = "Address", namespace = "http://example.org") 
    private Address address; 

    public String getName() { 
     return name; 
    } 

    public Address getAddress() { 
     return address; 
    } 
} 

Address.java:

public class Address { 
    @XmlElement(name = "StreetAddress", namespace = "http://example.org") 
    private String streetAddress; 
    @XmlElement(name = "PostalCode", namespace = "http://example.org") 
    private String postalCode; 
    @XmlElement(name = "CountryName", namespace = "http://example.org") 
    private String countryName; 

    public String getStreetAddress() { 
     return streetAddress; 
    } 

    public String getPostalCode() { 
     return postalCode; 
    } 

    public String getCountryName() { 
     return countryName; 
    } 
} 

PersonlistProcessor.java:

public class PersonlistProcessor { 
    public static void main(String[] args) throws Exception { 
     new PersonlistProcessor().processPersonlist(PersonlistProcessor.class 
       .getResourceAsStream("personlist.xml")); 
    } 

    // TODO: Instead of throws Exception, all exceptions should be wrapped 
    // inside runtime exception 
    public void processPersonlist(InputStream inputStream) throws Exception { 
     JAXBContext jaxbContext = JAXBContext.newInstance(Person.class); 
     XMLStreamReader xss = XMLInputFactory.newFactory().createXMLStreamReader(inputStream); 
     // Create unmarshaller 
     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
     // Go to next tag 
     xss.nextTag(); 
     // Require Personlist 
     xss.require(XMLStreamReader.START_ELEMENT, "http://example.org", "Personlist"); 
     // Go to next tag 
     while (xss.nextTag() == XMLStreamReader.START_ELEMENT) { 
      // Require Person 
      xss.require(XMLStreamReader.START_ELEMENT, "http://example.org", "Person"); 
      // Unmarshall person 
      Person person = (Person)unmarshaller.unmarshal(xss); 
      // Process person 
      processPerson(person); 
     } 
     // Require Personlist 
     xss.require(XMLStreamReader.END_ELEMENT, "http://example.org", "Personlist"); 
    } 

    private void processPerson(Person person) { 
     System.out.println(person.getName()); 
     System.out.println(person.getAddress().getCountryName()); 
    } 
} 
0

W SAX parser "wypycha" zdarzenia w twoim treserze, więc musisz wykonać wszystkie czynności porządkowe, jak tu jesteś. Alternatywą może być StAX (pakiet javax.xml.stream), który wciąż jest przesyłany strumieniowo, ale twój kod jest odpowiedzialny za "wyciąganie" zdarzeń z parsera. W ten sposób logika tego, jakie elementy są oczekiwane w jakiej kolejności, jest zakodowana w strumieniu sterowania twojego programu, a nie musi być jawnie reprezentowana w bajtach.

zależności od dokładnej struktury XML nie może być „środkowa droga” za pomocą zestawu narzędzi jak XOM, który posiada tryb pracy, w którym analizować poddrzewo dokumentu do obiektowego modelu DOM-like, proces, który gałązka, a następnie wyrzuć ją i przeanalizuj następną. Jest to dobre dla powtarzających się dokumentów z wieloma podobnymi elementami, z których każdy może być przetwarzany w izolacji - masz łatwość programowania do API opartego na drzewach w każdym drzewie, ale wciąż masz działanie strumieniowe, które pozwala wydajnie analizować wielkie dokumenty.

public class ItemProcessor extends NodeFactory { 
    private Nodes emptyNodes = new Nodes(); 

    public Nodes finishMakingElement(Element elt) { 
    if("Item".equals(elt.getLocalName())) { 
     // process the Item element here 
     System.out.println(elt.getFirstChildElement("ItemId").getValue() 
     + ": " + elt.getFirstChildElement("ItemName").getValue()); 

     // then throw it away 
     return emptyNodes; 
    } else { 
     return super.finishMakingElement(elt); 
    } 
    } 
} 

można osiągnąć coś podobnego z kombinacją Stax i JAXB - określić JAXB odnotowany klas, które reprezentują swój powtarzający elementu (pozycja w tym przykładzie), a następnie utworzyć parser Stax, przejdź do pierwszego znacznika Item startowym , a następnie można anulować jeden pełny Item naraz od XMLStreamReader.

-1
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.ArrayList; 
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.transform.Transformer; 
import javax.xml.transform.TransformerFactory; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathExpression; 
import javax.xml.xpath.XPathFactory; 
import org.w3c.dom.Document; 
import org.w3c.dom.NodeList; 

public class JXML { 
private DocumentBuilder builder; 
private Document doc = null; 
private DocumentBuilderFactory factory ; 
private XPathExpression expr = null; 
private XPathFactory xFactory; 
private XPath xpath; 
private String xmlFile; 
public static ArrayList<String> XMLVALUE ; 


public JXML(String xmlFile){ 
    this.xmlFile = xmlFile; 
} 


private void xmlFileSettings(){  
    try { 
     factory = DocumentBuilderFactory.newInstance(); 
     factory.setNamespaceAware(true); 
     xFactory = XPathFactory.newInstance(); 
     xpath = xFactory.newXPath(); 
     builder = factory.newDocumentBuilder(); 
     doc = builder.parse(xmlFile); 
    } 
    catch (Exception e){ 
     System.out.println(e); 
    }  
} 



public String[] selectQuery(String query){ 
    xmlFileSettings(); 
    ArrayList<String> records = new ArrayList<String>(); 
    try { 
     expr = xpath.compile(query); 
     Object result = expr.evaluate(doc, XPathConstants.NODESET); 
     NodeList nodes = (NodeList) result; 
     for (int i=0; i<nodes.getLength();i++){    
      records.add(nodes.item(i).getNodeValue()); 
     } 
     return records.toArray(new String[records.size()]); 
    } 
    catch (Exception e) { 
     System.out.println("There is error in query string"); 
     return records.toArray(new String[records.size()]); 
    }  
} 

public boolean updateQuery(String query,String value){ 
    xmlFileSettings(); 
    try{ 
     NodeList nodes = (NodeList) xpath.evaluate(query, doc, XPathConstants.NODESET); 
     for (int idx = 0; idx < nodes.getLength(); idx++) { 
      nodes.item(idx).setTextContent(value); 
     } 
     Transformer xformer = TransformerFactory.newInstance().newTransformer(); 
     xformer.transform(new DOMSource(doc), new StreamResult(new File(this.xmlFile))); 
     return true; 
    }catch(Exception e){ 
     System.out.println(e); 
     return false; 
    } 
} 




public static void main(String args[]){ 
    JXML jxml = new JXML("c://user.xml"); 
    jxml.updateQuery("//Order/CustomerId/text()","222"); 
    String result[]=jxml.selectQuery("//Order/Item/*/text()"); 
    for(int i=0;i<result.length;i++){ 
     System.out.println(result[i]); 
    } 
} 

}

+0

PO wyraźnie stwierdził, że nie chce używać DOM (ani żadnego innego modelu, który wymaga analizy całego dokumentu w strukturze drzewa w pamięci) –

6

Jeśli kontrolujesz definicji XML, można użyć XML funkcji wiążącej, na przykład JAXB (Java Architektura dla Wiązanie XML). W JAXB można zdefiniować schemat dla struktura XML (XSD i inne są obsługiwane) lub adnotacja klas Java w celu zdefiniowania reguł serializacji. Po uzyskaniu wyraźnego odwzorowania między XML a Javą porządkowanie i rozpoczynanie od/do XML staje się banalne.

Korzystanie z JAXB wymaga więcej pamięci niż procedury obsługi SAX, ale istnieją metody przetwarzania dokumentów XML według części: Dealing with large documents.

JAXB page from Oracle

0

Używam xsteam do serializacji własnych obiektów do XML, a następnie załadować je jako obiekty Java. Jeśli możesz reprezentować wszystko jako POJO i prawidłowo adnotujesz POJO, aby dopasować typy w twoim pliku xml, możesz znaleźć o wiele łatwiejszy w użyciu.

Kiedy String reprezentuje obiekt w formacie XML, można po prostu napisać:

Order theOrder = (Order)xstream.fromXML(xmlString);

zawsze używał go, aby załadować obiekt do pamięci w jednej linii, ale jeśli trzeba go strumieniowo i przetwarzaj w trakcie pracy, powinieneś móc użyć HierarchicalStreamReader do iteracji dokumentu. Może to być bardzo podobne do Simple, sugerowane przez @Dave.

0

Jak sugerowali inni, model Stax byłby lepszym podejściem do minimalizacji drukowania stóp pamięci, ponieważ jest to model oparty na push. Osobiście użyłem Axio (Który jest używany w Apache Axis) i parsuję elementy używając wyrażeń XPath, które są mniej szczegółowe niż przechodzenie przez elementy węzła, jak to zrobiłeś w podanym fragmencie kodu.

0

Jest inna biblioteka, która obsługuje bardziej zwartą analizowania XML, RTXML. Biblioteka i jej dokumentacja znajduje się pod adresem rasmustorkel.com. I wdrożone parsowania pliku w oryginalnym pytanie, a ja w tym kompletny program tutaj:

package for_so; 

import java.io.File; 
import java.util.ArrayList; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

import rasmus_torkel.xml_basic.read.TagNode; 
import rasmus_torkel.xml_basic.read.XmlReadOptions; 
import rasmus_torkel.xml_basic.read.impl.XmlReader; 

public class Q15626686_ReadOrder 
{ 
    public static class Order 
    { 
     public final Date   _date; 
     public final int    _customerId; 
     public final String   _customerName; 
     public final ArrayList<Item> _itemAl; 

     public 
     Order(TagNode node) 
     { 
      _date = (Date)node.nextStringMappedFieldE("Date", Date.class); 
      _customerId = (int)node.nextIntFieldE("CustomerId"); 
      _customerName = node.nextTextFieldE("CustomerName"); 
      _itemAl = new ArrayList<Item>(); 
      boolean finished = false; 
      while (!finished) 
      { 
       TagNode itemNode = node.nextChildN("Item"); 
       if (itemNode != null) 
       { 
        Item item = new Item(itemNode); 
        _itemAl.add(item); 
       } 
       else 
       { 
        finished = true; 
       } 
      } 
      node.verifyNoMoreChildren(); 
     } 
    } 

    public static final Pattern DATE_PATTERN = Pattern.compile("^(\\d\\d\\d\\d)\\/(\\d\\d)\\/(\\d\\d)$"); 

    public static class Date 
    { 
     public final String _dateString; 
     public final int _year; 
     public final int _month; 
     public final int _day; 

     public 
     Date(String dateString) 
     { 
      _dateString = dateString; 
      Matcher matcher = DATE_PATTERN.matcher(dateString); 
      if (!matcher.matches()) 
      { 
       throw new RuntimeException(dateString + " does not match pattern " + DATE_PATTERN.pattern()); 
      } 
      _year = Integer.parseInt(matcher.group(1)); 
      _month = Integer.parseInt(matcher.group(2)); 
      _day = Integer.parseInt(matcher.group(3)); 
     } 
    } 

    public static class Item 
    { 
     public final int  _itemId; 
     public final String _itemName; 
     public final Quantity _quantity; 

     public 
     Item(TagNode node) 
     { 
      _itemId = node.nextIntFieldE("ItemId"); 
      _itemName = node.nextTextFieldE("ItemName"); 
      _quantity = new Quantity(node.nextChildE("Quantity")); 
      node.verifyNoMoreChildren(); 
     } 
    } 

    public static class Quantity 
    { 
     public final int _unitSize; 
     public final int _unitQuantity; 

     public 
     Quantity(TagNode node) 
     { 
      _unitSize = node.attributeIntD("unit", 1); 
      _unitQuantity = node.onlyInt(); 
     } 
    } 

    public static void 
    main(String[] args) 
    { 
     File xmlFile = new File(args[0]); 
     TagNode orderNode = XmlReader.xmlFileToRoot(xmlFile, "Order", XmlReadOptions.DEFAULT); 
     Order order = new Order(orderNode); 
     System.out.println("Read order for " + order._customerName + " which has " + order._itemAl.size() + " items"); 
    } 
} 

Można zauważyć, że funkcje pobierania zakończyć w N, E lub D. Odnoszą się one do tego, co zrobić, gdy pożądany element danych nie istnieje. N oznacza powrót Null, E oznacza rzut. Wyjątek i D oznacza użycie Domyślne.

0

rozwiązanie bez korzystania z opakowania zewnętrznego, a nawet XPath: Stosuje "PARSE_MODE" enum, prawdopodobnie w połączeniu z Stack<PARSE_MODE>:

1) Podstawowy roztwór:

a) pola

private PARSE_MODE parseMode = PARSE_MODE.__UNDEFINED__; 
// NB: essential that all these enum values are upper case, but this is the convention anyway 
private enum PARSE_MODE { 
    __UNDEFINED__, ORDER, DATE, CUSTOMERID, ITEM }; 
private List<String> parseModeStrings = new ArrayList<String>(); 
private Stack<PARSE_MODE> modeBreadcrumbs = new Stack<PARSE_MODE>(); 

b) dokonać List<String>, może w konstr Tor:

for(PARSE_MODE pm : PARSE_MODE.values()){ 
     // might want to check here that these are indeed upper case 
     parseModeStrings.add(pm.name()); 
    } 

c) startElement i endElement:

@Override 
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { 
    String localNameUC = localName.toUpperCase(); 
    // pushing "__UNDEFINED__" would mess things up! But unlikely name for an XML element 
    assert ! localNameUC.equals("__UNDEFINED__"); 

    if(parseModeStrings.contains(localNameUC)){ 
     parseMode = PARSE_MODE.valueOf(localNameUC); 
     // any "policing" to do with which modes are allowed to switch into 
     // other modes could be put here... 
     // in your case, go `new Order()` here when parseMode == ORDER 
     modeBreadcrumbs.push(parseMode); 
    } 
    else { 
     // typically ignore the start of this element... 
    } 
} 

@Override 
private void endElement(String uri, String localName, String qName) throws Exception { 
    String localNameUC = localName.toUpperCase(); 
    if(parseModeStrings.contains(localNameUC)){ 
     // will not fail unless XML structure which is malformed in some way 
     // or coding error in use of the Stack, etc.: 
     assert modeBreadcrumbs.pop() == parseMode; 
     if(modeBreadcrumbs.empty()){ 
      parseMode = PARSE_MODE.__UNDEFINED__; 
     } 
     else { 
      parseMode = modeBreadcrumbs.peek(); 
     } 
    } 
    else { 
     // typically ignore the end of this element... 
    } 

} 

... więc co to wszystko znaczy? W dowolnym momencie masz wiedzę na temat "trybu parsowania", w którym się znajdujesz ... i możesz także spojrzeć na Stack<PARSE_MODE> modeBreadcrumbs, jeśli chcesz dowiedzieć się, jakie inne tryby analizy parsowej przeszedłeś, aby uzyskać tutaj ...

Twoja metoda characters staje się znacznie czystsze:

public void characters(char[] ch, int start, int length) throws SAXException { 
    switch(parseMode){ 
    case DATE: 
     // PS - this SimpleDateFormat object can be a field: it doesn't need to be created hundreds of times 
     SimpleDateFormat formatter. ... 
     String value = ... 
     ... 
     break; 

    case CUSTOMERID: 
     order.setCustomerId(... 
     break; 

    case ITEM: 
     item = new Item(); 
     // this next line probably won't be needed: when you get to endElement, if 
     // parseMode is ITEM, the previous mode will be restored automatically 
     // isItem = false ; 
    } 

} 

2) Im bardziej „profesjonalne” rozwiązanie:
abstract klasa która betonu klasy mają rozszerzyć i który następnie nie mają zdolność do modyfikowania Stack, etc. NB to jest analizowane jako qName zamiast localName. Zatem:

public abstract class AbstractSAXHandler extends DefaultHandler { 
    protected enum PARSE_MODE implements SAXHandlerParseMode { 
     __UNDEFINED__ 
    }; 
    // abstract: the concrete subclasses must populate... 
    abstract protected Collection<Enum<?>> getPossibleModes(); 
    // 
    private Stack<SAXHandlerParseMode> modeBreadcrumbs = new Stack<SAXHandlerParseMode>(); 
    private Collection<Enum<?>> possibleModes; 
    private Map<String, Enum<?>> nameToEnumMap; 
    private Map<String, Enum<?>> getNameToEnumMap(){ 
     // lazy creation and population of map 
     if(nameToEnumMap == null){ 
      if(possibleModes == null){ 
       possibleModes = getPossibleModes(); 
      } 
      nameToEnumMap = new HashMap<String, Enum<?>>(); 
      for(Enum<?> possibleMode : possibleModes){ 
       nameToEnumMap.put(possibleMode.name(), possibleMode); 
      } 
     } 
     return nameToEnumMap; 
    } 

    protected boolean isLegitimateModeName(String name){ 
     return getNameToEnumMap().containsKey(name); 
    } 

    protected SAXHandlerParseMode getParseMode() { 
     return modeBreadcrumbs.isEmpty()? PARSE_MODE.__UNDEFINED__ : modeBreadcrumbs.peek(); 
    } 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes attributes) 
      throws SAXException { 
     try { 
      _startElement(uri, localName, qName, attributes); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    // override in subclasses (NB I think caught Exceptions are not a brilliant design choice in Java) 
    protected void _startElement(String uri, String localName, String qName, Attributes attributes) 
      throws Exception { 
     String qNameUC = qName.toUpperCase(); 
     // very undesirable ever to push "UNDEFINED"! But unlikely name for an XML element 
     assert !qNameUC.equals("__UNDEFINED__") : "Encountered XML element with qName \"__UNDEFINED__\"!"; 
     if(getNameToEnumMap().containsKey(qNameUC)){ 
      Enum<?> newMode = getNameToEnumMap().get(qNameUC); 
      modeBreadcrumbs.push((SAXHandlerParseMode)newMode); 
     } 
    } 

    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 
     try { 
      _endElement(uri, localName, qName); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    // override in subclasses 
    protected void _endElement(String uri, String localName, String qName) throws Exception { 
     String qNameUC = qName.toUpperCase(); 
     if(getNameToEnumMap().containsKey(qNameUC)){ 
      modeBreadcrumbs.pop(); 
     } 
    } 

    public List<?> showModeBreadcrumbs(){ 
     return org.apache.commons.collections4.ListUtils.unmodifiableList(modeBreadcrumbs); 
    } 

} 

interface SAXHandlerParseMode { 

} 

Następnie Istotną częścią konkretnej podklasy:

private enum PARSE_MODE implements SAXHandlerParseMode { 
    ORDER, DATE, CUSTOMERID, ITEM 
}; 

private Collection<Enum<?>> possibleModes; 

@Override 
protected Collection<Enum<?>> getPossibleModes() { 
    // lazy initiation 
    if (possibleModes == null) { 
     List<SAXHandlerParseMode> parseModes = new ArrayList<SAXHandlerParseMode>(Arrays.asList(PARSE_MODE.values())); 
     possibleModes = new ArrayList<Enum<?>>(); 
     for(SAXHandlerParseMode parseMode : parseModes){ 
      possibleModes.add(PARSE_MODE.valueOf(parseMode.toString())); 
     } 
     // __UNDEFINED__ mode (from abstract superclass) must be added afterwards 
     possibleModes.add(AbstractSAXHandler.PARSE_MODE.__UNDEFINED__); 
    } 
    return possibleModes; 
} 

PS jest to punkt wyjścia do bardziej wyrafinowanych rzeczy: na przykład można założyć List<Object> który jest zsynchronizowany z Stack<PARSE_MODE>: Objects może być dowolna, umożliwiając "powrót do" wstępnych "węzłów XML" tego, z którym masz do czynienia. Nie należy jednak używać urządzenia Map: obiekt Stack może zawierać ten sam obiekt PARSE_MODE więcej niż jeden raz. To w rzeczywistości przedstawia zasadniczą cechą wszystkich struktur drzewiastych, takich jak: żadna jednostka węzła(tu: analizowania tryb)istnieje w izolacji: jego tożsamość jest zawsze określona przez całą ścieżkę prowadzącą do niego.