Problem context: I had a big size xml like below where i wanted to identify employee with wrong department Id. I needed an implementation which is not slow like DOM and still can update XML like DOM, I wanted code fast like SAX but still can travel in future to read Department nodes like DOM.
Example:
<MyTeam> <Employee depId="1">Samarth</Employee> <Employee depId="1">Het</Employee> <Employee depId="999">Marketing_Employee</Employee> <Employee depId="1">Sahil</Employee> <Employee depId="1">Jainil/Jaimin</Employee> <Employee depId="1">Freya</Employee> <Department id="192">Engineering</Department> </MyTeam>
After Execution:
<?xml version="1.0"?> <MyTeam> <Employee depId="1">Samarth</Employee> <Employee depId="1">Het</Employee> <Employee depId="999">Marketing_Employee-CHANGEDTO-192<Comment WrongValue="Marketing_Employee">DepartmentUpdated</Comment></Employee> <Employee depId="1">Sahil</Employee> <Employee depId="1">Jainil/Jaimin</Employee> <Employee depId="1">Freya</Employee> <Department id="192">Engineering</Department> </MyTeam>
Implementation:
import java.io.ByteArrayInputStream; import java.io.File; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; public class STAXParserMultiPointerReadAndRealtimeWrite { private static final String input= "<MyTeam>" + "<Employee depId=\"1\">Samarth</Employee>"+ "<Employee depId=\"1\">Het</Employee>"+ "<Employee depId=\"999\">Marketing_Employee</Employee>"+ "<Employee depId=\"1\">Sahil</Employee>"+ "<Employee depId=\"1\">Jainil/Jaimin</Employee>"+ "<Employee depId=\"1\">Freya</Employee>"+ "<Department id=\"192\">Engineering</Department>"+ "</MyTeam>"; private static final String EMPLOYEE = "Employee"; private static final String DEPARTMENT = "Department"; private static final String DEPT_ID = "id"; private static final String EMP_DEPTID = "depId"; private static class MyXMLHandler{ private XMLEventReader reader = null, readerDepartment = null; private XMLEventWriter writer = null; private int matchingPointerDepartment=0; private XMLEvent eventToWrite=null; // private FileOutputStream fos = null; public MyXMLHandler(File file, File outputFile) throws Exception{ reader = XMLInputFactory.newInstance().createXMLEventReader(new ByteArrayInputStream(input.getBytes())); //new FileInputStream(file)); readerDepartment = XMLInputFactory.newInstance().createXMLEventReader(new ByteArrayInputStream(input.getBytes())); //new FileInputStream(file)); // fos = new FileOutputStream(outputFile); writer = XMLOutputFactory.newInstance().createXMLEventWriter(System.out); } public XMLEvent nextEvent() throws Exception{ if(matchingPointerDepartment==0){ readerDepartment.next(); } else{ matchingPointerDepartment--;//To bring both pointer on same place. } if(eventToWrite!=null){ writerIt(eventToWrite); //Write last event if it was not changed } eventToWrite=(XMLEvent)reader.next(); return eventToWrite; } public XMLEvent futureNextForDepartment(){ matchingPointerDepartment++;//To bring back pointer on same place first you have to measure how far you are in future. return (XMLEvent)readerDepartment.next(); } public boolean hasNext(){ return reader.hasNext(); } public void writerIt(XMLEvent event) throws Exception{//Only use to write update in XML. eventToWrite=null; writer.add(event); } public void generateFile() throws Exception{ writer.flush(); writer.close(); // fos.close(); } } private static void createCommentTags(MyXMLHandler myFile, XMLEventFactory eventFactory, String oldValue) throws Exception { myFile.writerIt(eventFactory.createStartElement(new QName("Comment"), null, null)); myFile.writerIt(eventFactory.createAttribute("WrongValue", oldValue)); myFile.writerIt(eventFactory.createCharacters("DepartmentUpdated")); myFile.writerIt(eventFactory.createEndElement(new QName("Comment"), null)); } public static void main(String[] args) throws Exception { XMLEventFactory eventFactory = XMLEventFactory.newInstance(); MyXMLHandler myFile = new MyXMLHandler(new File("employee.xml"), new File("new_employee.xml")); String departmentId=null; boolean employeeStarted = false; boolean wrongDept=false; while(myFile.hasNext()) { XMLEvent event = myFile.nextEvent(); switch (event.getEventType()) { case XMLStreamConstants.START_ELEMENT: StartElement startElement = event.asStartElement(); String startElementName = startElement.getName().getLocalPart(); if(EMPLOYEE.equals(startElementName)){ employeeStarted = true; if(departmentId==null) departmentId=findDepartmentId(myFile); Attribute attribute = startElement.getAttributeByName(new QName(EMP_DEPTID)); if(!departmentId.equals(attribute.getValue())){ wrongDept=true; } } break; case XMLStreamConstants.END_ELEMENT: String endElementName = event.asEndElement().getName().getLocalPart(); if(EMPLOYEE.equals(endElementName)){ employeeStarted=false; } break; case XMLStreamConstants.CHARACTERS: if(wrongDept){ String itValue = event.asCharacters().getData(); myFile.writerIt(eventFactory.createCharacters(itValue+"-CHANGEDTO-"+departmentId)); createCommentTags(myFile, eventFactory, itValue); wrongDept=false; } break; } } myFile.generateFile(); } private static String findDepartmentId(MyXMLHandler myFile) throws Exception{ XMLEvent event = myFile.futureNextForDepartment(); while(!event.isEndDocument()){ if(event.getEventType() == XMLStreamConstants.START_ELEMENT){ StartElement startElement = event.asStartElement(); String elementName = startElement.getName().getLocalPart(); if(DEPARTMENT.equals(elementName)){ Attribute attribute = startElement.getAttributeByName(new QName(DEPT_ID)); if(attribute!=null){ return attribute.getValue(); } } } event = myFile.futureNextForDepartment(); } throw new RuntimeException("Couldn't find department."); } }