Fastest way to update big size XML in Java

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.");
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *