2016-09-15 29 views
0

我需要批量寫入數據到xml。Spring Jaxb2:如何將批處理數據追加到XML文件中而不將它讀到內存中?

有以下域對象:

@XmlRootElement(name = "country") 
public class Country { 
    @XmlElements({@XmlElement(name = "town", type = Town.class)}) 
    private Collection<Town> towns = new ArrayList<>(); 
    .... 
} 

和:

@XmlRootElement(name = "town") 
public class Town { 
    @XmlElement 
    private String townName; 
    // etc 
} 

我marhalling對象與JAXB2。配置如下:

marshaller = new Jaxb2Marshaller(); 
marshaller.setClassesToBeBound(Country.class, Town.class); 

因爲簡單編組不在這裏工作作爲marhaller.marshall(fileName, country) - 它malformes XML。

有沒有辦法馴服marhaller,以便它會創建文件,如果它不存在與所有marhalled數據或如果存在只是追加它在xml文件的末尾?

此外,由於這些文件可能很大,我不想在內存中讀取整個文件,追加數據,然後寫入磁盤。

回答

1

我已經使用StAX進行xml處理,因爲它基於流,消耗更少的內存然後DOM,並且具有讀寫能力,與只能解析xml數據的SAX相比,但不能寫入。

的是我想出了辦法:

public enum StAXBatchWriter { 
    INSTANCE; 
    private static final Logger LOGGER = LoggerFactory.getLogger(StAXBatchWriter.class); 

    public void writeUrls(File original, Collection<Town> towns) { 
     XMLEventReader eventReader = null; 
     XMLEventWriter eventWriter = null; 
     try { 
      String originalPath = original.getPath(); 
      File from = new File(original.getParent() + "/old-" + original.getName()); 
      boolean isRenamed = original.renameTo(from); 
      if (!isRenamed) 
       throw new IllegalStateException("Failed to rename file: " + original.getPath() + " to " + from.getPath()); 
      File to = new File(originalPath); 

      XMLInputFactory inFactory = XMLInputFactory.newInstance(); 
      eventReader = inFactory.createXMLEventReader(new FileInputStream(from)); 

      XMLOutputFactory outFactory = XMLOutputFactory.newInstance(); 
      eventWriter = outFactory.createXMLEventWriter(new FileWriter(to)); 

      XMLEventFactory eventFactory = XMLEventFactory.newInstance(); 

      while (eventReader.hasNext()) { 
       XMLEvent event = eventReader.nextEvent(); 
       eventWriter.add(event); 
       if (event.getEventType() == XMLEvent.START_ELEMENT && event.asStartElement().getName().toString().contains("country")) { 
        for (Town town : towns) { 
         writeTown(eventWriter, eventFactory, town); 
        } 
       } 
      } 
      boolean isDeleted = from.delete(); 
      if (!isDeleted) 
       throw new IllegalStateException("Failed to delete old file: " + from.getPath()); 
     } catch (IOException | XMLStreamException e) { 
      LOGGER.error(e.getMessage(), e); 
      throw new RuntimeException(e); 
     } finally { 
      try { 
       if (eventReader != null) 
        eventReader.close(); 
      } catch (XMLStreamException e) { 
       LOGGER.error(e.getMessage(), e); 
      } 
      try { 
       if (eventWriter != null) 
        eventWriter.close(); 
      } catch (XMLStreamException e) { 
       LOGGER.error(e.getMessage(), e); 
      } 
     } 
    } 

    private void writeTown(XMLEventWriter eventWriter, XMLEventFactory eventFactory, Town town) throws XMLStreamException { 
     eventWriter.add(eventFactory.createStartElement("", null, "town")); 

     // write town id 
     eventWriter.add(eventFactory.createStartElement("", null, "id")); 
     eventWriter.add(eventFactory.createCharacters(town.getId())); 
     eventWriter.add(eventFactory.createEndElement("", null, "id")); 

     //write town name 
     if (StringUtils.isNotEmpty(town.getName())) { 
      eventWriter.add(eventFactory.createStartElement("", null, "name")); 
      eventWriter.add(eventFactory.createCharacters(town.getName())); 
      eventWriter.add(eventFactory.createEndElement("", null, "name")); 
     } 

     // write other fields 

     eventWriter.add(eventFactory.createEndElement("", null, "town")); 
    } 
} 

這是不是最好的方法,dispite的事實,這是基於流,它的工作,它具有一些開銷。當一個批次將被添加 - 舊文件必須重新讀取。

這將是很高興有一個選項來追加數據在文件中的某個點(如「追加數據到該文件後4行」),但似乎這是不能做到的。

相關問題