JaxB marshaler覆盖文件内容

时间:2015-03-19 01:31:50

标签: java xml jaxb

我正在尝试使用JaxB来编组我创建的XML对象。我想要的是创建一个列表,然后将其打印到文件,然后创建一个新列表并将其打印到同一个文件,但每次我执行它都会写入第一个。我希望最终的XML文件看起来像我只有一个大的对象列表。我会这样做,但有很多,我迅速扩大我的堆大小。

所以,我的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;
}

1 个答案:

答案 0 :(得分:1)

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

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

所以你能做什么,

  • 是使用手动启动的root创建新的xml表示 元件,
    • 使用XMLStreamWriter / XMLStreamReader读/写操作或解组将现有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的完整实现: https://stackoverflow.com/a/9260039/4483840

这就是优雅的&#39;解。 不太优雅的是让你的线程将他们的日志列表写入单个文件并自己附加它们。您只需要读取并复制第一个文件的标题,然后复制除最后一个结束标记之外的所有内容,复制其他文件的内容而忽略文档openkng和结束标记,输出结束标记。

如果您的marshaller设置为marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true); 每个开/关标签将在不同的行,所以丑陋的黑客是 将所有行从第3行复制到最后一行,然后输出结束标记。

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