As we know from Brooks' essay, in software engineering there is no such thing as a silver bullet; at least, not one that doesn't come in a 12oz can. That's why XML is suspicious: no one should have spent the time to develop a single, consistent methodology for data interchange when any possible effort could only be in vain. It is unfortunate that this so-called panacea has caught on: it's so easy that companies feel like they can get away with hiring developers instead of engineers.

To take this article down another notch: engineers usually understand the project triangle: good, fast, cheap: pick two. If you don't understand what I'm talking about, just say "XML should work" when anyone asks you a question and you'll be fine. The real problem with XML is transforming it into something more usable. It'd be really nice if someone came up with a transformation language for XML, but I suspect that this would fail for the reasons stated above.

Because of this embarrassing omission on the part of the W3C, developers are forced to parse and transform XML on their own. Jean, a contractor for a Fortune 500 company, has recently been asked to cut-and-paste this kind of functionality into a J2EE application. Here's an example of the way they deal with the XML returned by their internal web services.

 

  try{
  while(xmlr.hasNext()){
      xmlr.next();
      if(xmlr.getEventType()==XMLStreamConstants.END_ELEMENT
              && xmlr.getLocalName().equalsIgnoreCase("status"))
          break;
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-car-status-code"))
         
status.setCurrentCarStatusCode(getStringNodeValue("current-car-status-code",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-yard-number"))
         
status.setCurrentYardNumber((int)getNumberNodeValue("current-yard-number",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-track-number"))
         
status.setCurrentTrackNumber((int)getNumberNodeValue("current-track-number",xmlr)); 
               
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-spot-or-sequence-number"))
         
status.setCurrentSpotSeqNumber((int)getNumberNodeValue("current-spot-or-sequence-number",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-track-sequence-number"))
         
status.setCurrentTrackSeqNumber((int)getNumberNodeValue("current-track-sequence-number",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("maximum-track-sequence-number"))
         
status.setMaximumTrackSeqNumber((int)getNumberNodeValue("maximum-track-sequence-number",xmlr));   
        
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-carrier-abbreviation"))
         
status.setCurrentCarrierAbbreviation(getStringNodeValue("current-carrier-abbreviation",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("offline-junction-carrier-abbreviation"))
         
status.setOfflineJunctionCarrierAbbreviation(getStringNodeValue("offline-junction-carrier-abbreviation",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-load-empty-code"))
         
status.setCurrentLoadEmptyCode(getStringNodeValue("current-load-empty-code",xmlr));   
                 
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("final-yard-number"))
         
status.setFinalYardNumber((int)getNumberNodeValue("final-yard-number",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("final-track-number"))
         
status.setFinalTrackNumber((int)getNumberNodeValue("final-track-number",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("final-spot-or-sequence-number"))
         
status.setFinalSpotSeqNumber((int)getNumberNodeValue("final-spot-or-sequence-number",xmlr));   
        
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("up-location-code"))
         
status.setUpLocationCode(getStringNodeValue("up-location-code",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("equipment-type"))
         
status.setEquipmentType(getStringNodeValue("equipment-type",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("TCS-car-kind"))
         
status.setTcsCarKind(getStringNodeValue("TCS-car-kind",xmlr));            
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("consignee-633-name"))
         
status.setConsignee633Name(getStringNodeValue("consignee-633-name",xmlr)); 
            
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("previous-stcc-code"))
         
status.setPreviousStccCode(getStringNodeValue("previous-stcc-code",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              && xmlr.getLocalName().equalsIgnoreCase("stcc-code"))
          status.setStccCode(getStringNodeValue("stcc-code",xmlr));     
        
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("previous-commodity-abbreviation"))
         
status.setPreviousCommodityAbbreviation(getStringNodeValue("previous-commodity-abbreviation",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("commodity-abbreviation"))
         
status.setCommodityAbbreviation(getStringNodeValue("commodity-abbreviation",xmlr)); 
            
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("FSD-arrival-date-time"))
         
status.setFSDArrivalDateTime(setDate(getStringNodeValue("FSD-arrival-date-time",xmlr)));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("latest-planned-event-date-time"))
         
status.setLatestPlannedEventDateTime(setDate(getStringNodeValue("latest-planned-event-date-time",xmlr)));   
  
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("FSD-system-location"))
         
status.setFsdSystemLocation(processLocationNode("FSD-system-location",xmlr));   
        
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("NSD-system-location"))
         
status.setNsdSystemLocation(processLocationNode("NSD-system-location",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("after-NSD-system-location"))
         
status.setAfterNsdSystemLocation(processLocationNode("after-NSD-system-location",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("origin-system-location"))
         
status.setOriginalSystemLocation(processLocationNode("origin-system-location",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("final-location"))
         
status.setFinalLocation(processLocationNode("final-location",xmlr));  
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("final-offline-location"))
         
status.setFinalOfflineLocation(processLocationNode("final-offline-location",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("origin-location"))
         
status.setOriginLocation(processLocationNode("origin-location",xmlr));  
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("origin-offline-location"))
         
status.setOriginOfflineLocation(processLocationNode("origin-offline-location",xmlr));   

      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-location"))
         
status.setCurrentLocation(processLocationNode("current-location",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("current-offline-location"))
         
status.setCurrentOfflineLocation(processLocationNode("current-offline-location",xmlr));   
        
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("consignee-location"))
         
status.setConsigneeLocation(processLocationNode("consignee-location",xmlr));   

      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("special-condition-code-list"))
         
status.setSpecialConditionCodes(processSpecialConditionNode("special-condition-code-list",xmlr));
      else if(xmlr.getEventType()==XMLStreamConstants.START_ELEMENT
              &&
xmlr.getLocalName().equalsIgnoreCase("latest-reported-event"))
         
status.setLastEvent(processEventNode("latest-reported-event",xmlr));   
            
      }
  }catch(Exception e){}
  return status;

 

Yes, that's StAX. Now, what you've been waiting for: the nasty part. The reason they're not using XPath? The returned data is pretty flat, so the size of a DOM parse tree is not an issue. It turns out that the developers thought XPath was too complicated, so they came up with a solution similar to the code above. The prototype solution is now a template for all new web service processors, some of which return hundreds of different tags. All of those process* functions shove data into HashMaps, for easy access.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!