JaxB 封送拆收器覆盖文件内容

JaxB marshaler overwriting file contents

我正在尝试使用 JaxB 将我创建的对象编组到 XML。我想要的是创建一个列表,然后将其打印到文件,然后创建一个新列表并将其打印到同一个文件,但每次我这样做都会覆盖第一个。我希望最终的 XML 文件看起来只有 1 个大对象列表。我会这样做,但有太多我很快就会最大化我的堆大小。

所以,我的 main 创建了一堆线程,每个线程循环访问它接收的对象列表,并对每个对象调用 create_Log。完成后,它会调用 printToFile,这是它将列表编组到文件的地方。

public class LogThread implements Runnable {
//private Thread myThread;
private Log_Message message = null;
private LinkedList<Log_Message> lmList = null;
LogServer Log = null;
private String Username = null;

public LogThread(LinkedList<Log_Message> lmList){
    this.lmList = lmList;
}

public void run(){
    //System.out.println("thread running");
    LogServer Log = new LogServer();
    //create iterator for list
    final ListIterator<Log_Message> listIterator = lmList.listIterator();

    while(listIterator.hasNext()){
        message = listIterator.next();
        CountTrans.addTransNumber(message.TransactionNumber);
        Username = message.input[2];
        Log.create_Log(message.input, message.TransactionNumber, message.Message, message.CMD);
    }
    Log.printToFile();
    init_LogServer.threadCount--;
    init_LogServer.doneList();
    init_LogServer.doneUser();
    System.out.println("Thread "+ Thread.currentThread().getId() +" Completed user: "+ Username+"... Number of Users Complete: " + init_LogServer.getUsersComplete());
    //Thread.interrupt();
}
}

上面调用了下面的函数 create_Log 来构建一个新对象,我从给定的 XSD (SystemEventType、QuoteServerType...等)中生成。这些对象都使用下面的函数添加到 ArrayList 并附加到 Root 对象。一旦 LogThread 循环完成,它就会调用 printToFile,它从 Root 对象中获取列表并将其编组到文件中……覆盖已经存在的内容。我怎样才能将它添加到同一个文件而不重写并且不在堆中创建一个主列表?

public class LogServer {
public log Root = null;
public static String fileName = "LogFile.xml";
public static File XMLfile = new File(fileName);

public LogServer(){
    this.Root = new log();
}
//output LogFile.xml
public synchronized void printToFile(){
    System.out.println("Printing XML");
    //write to xml file
    try {
        init_LogServer.marshaller.marshal(Root,XMLfile);
    } catch (JAXBException e) {
        e.printStackTrace();
    }
    System.out.println("Done Printing XML");
}


private BigDecimal ConvertStringtoBD(String input){
    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
    symbols.setGroupingSeparator(',');
    symbols.setDecimalSeparator('.');
    String pattern = "#,##0.0#";
    DecimalFormat decimalFormat = new DecimalFormat(pattern, symbols);
    decimalFormat.setParseBigDecimal(true);
    // parse the string
    BigDecimal bigDecimal = new BigDecimal("0");
    try {
        bigDecimal = (BigDecimal) decimalFormat.parse(input);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return bigDecimal;
}

public QuoteServerType Log_Quote(String[] input, int TransactionNumber){
    BigDecimal quote = ConvertStringtoBD(input[4]);
    BigInteger TransNumber = BigInteger.valueOf(TransactionNumber);
    BigInteger ServerTimeStamp = new BigInteger(input[6]);
    Date date = new Date();
    long timestamp = date.getTime();
    ObjectFactory factory = new ObjectFactory();
    QuoteServerType quoteCall = factory.createQuoteServerType();

    quoteCall.setTimestamp(timestamp);
    quoteCall.setServer(input[8]);
    quoteCall.setTransactionNum(TransNumber);
    quoteCall.setPrice(quote);
    quoteCall.setStockSymbol(input[3]);
    quoteCall.setUsername(input[2]);
    quoteCall.setQuoteServerTime(ServerTimeStamp);
    quoteCall.setCryptokey(input[7]);

    return quoteCall;
}
public SystemEventType Log_SystemEvent(String[] input, int TransactionNumber, CommandType CMD){
    BigInteger TransNumber = BigInteger.valueOf(TransactionNumber);
    Date date = new Date();
    long timestamp = date.getTime();
    ObjectFactory factory = new ObjectFactory();
    SystemEventType SysEvent = factory.createSystemEventType();

    SysEvent.setTimestamp(timestamp);
    SysEvent.setServer(input[8]);
    SysEvent.setTransactionNum(TransNumber);
    SysEvent.setCommand(CMD);
    SysEvent.setFilename(fileName);

    return SysEvent;
}

public void create_Log(String[] input, int TransactionNumber, String Message, CommandType Command){
    switch(Command.toString()){
    case "QUOTE":  //Quote_Log
        QuoteServerType quote_QuoteType = Log_Quote(input,TransactionNumber);
        Root.getUserCommandOrQuoteServerOrAccountTransaction().add(quote_QuoteType);
        break;

    case "QUOTE_CACHED":
        SystemEventType Quote_Cached_SysType = Log_SystemEvent(input, TransactionNumber, CommandType.QUOTE);
        Root.getUserCommandOrQuoteServerOrAccountTransaction().add(Quote_Cached_SysType);
        break;

}
}

编辑:下面是如何将对象添加到 ArrayList

的代码
    public List<Object> getUserCommandOrQuoteServerOrAccountTransaction() {
    if (userCommandOrQuoteServerOrAccountTransaction == null) {
        userCommandOrQuoteServerOrAccountTransaction = new ArrayList<Object>();
    }
    return this.userCommandOrQuoteServerOrAccountTransaction;
}

Jaxb 是关于将 java 对象树映射到 xml 文档,反之亦然。所以原则上,你需要完整的对象模型才能保存到xml。 当然这是不可能的,对于非常大的数据,例如 DB 转储,因此 jaxb 允许在片段中编组对象树,让用户控制对象创建和编组的时刻。典型的用例是从数据库中一条一条地获取记录并将它们一条一条地编组到一个文件中,因此堆不会有问题。

但是,您询问的是将一个对象树附加到另一个对象树(一个在内存中是新鲜的,第二个已经在 xml 文件中表示)。这通常是不可能的,因为它不是真正的追加,而是创建包含两者内容的新对象树(只有一个文档根元素,而不是两个)。

那么你可以做什么,

  • 是用手动启动的根创建新的xml表示 元素,
    • 使用 XMLStreamWriter/XMLStreamReader read/write 操作或解组将现有 xml 内容复制到新的 xml 日志对象并将它们一一编组。
    • 将您的日志对象编组到同一个 xml stram
    • 用根结束元素完成 xml。 -

大概是这样的:

XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(...), StandardCharsets.UTF_8.name());
//"mannually" output the beginign of the xml document == its declaration and the root element
writer.writeStartDocument();
writer.writeStartElement("YOUR_ROOT_ELM");

Marshaller mar = ...
mar.setProperty(Marshaller.JAXB_FRAGMENT, true); //instructs jaxb to output only objects not the whole xml document

PartialUnmarshaler existing = ...; //allows reading one by one xml content from existin file, 

while (existing.hasNext()) {
    YourObject obj = existing.next();
    mar.marshal(obj, writer);
    writer.flush();
}

List<YourObject> toAppend = ...
for (YourObject toAppend) {
    mar.marshal(obj,writer);
    writer.flush();
}

//finishing the document, closing the root element
writer.writeEndElement();
writer.writeEndDocument();

从大型 xml 文件中一一读取对象,并在此答案中描述了 PartialUnmarshaler 的完整实现:

这就是'elegant'的解决方法。 不太优雅的是让你的线程将它们的日志列表写入单独的文件并自己附加它们。你只需要读取并复制第一个文件的文件头,然后复制它除了最后一个结束标签之外的所有内容,复制其他文件的内容忽略文件openkng和结束标签,输出结束标签。

如果您的编组器设置为 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 每个 opening/closing 标签将在不同的行中,所以丑陋的技巧是 把第三行全部复制到最后一行,然后输出结束标签。

这是丑陋的 hack,因为它对您的输出格式很敏感(例如,如果您更改容器根元素)。但比完整的 Jaxb 解决方案实施起来更快。