一个流中有多个XML“文件”

时间:2011-07-15 18:45:06

标签: java xml-serialization jaxb

在为webservice开发适配器时,我最终面临这样的响应:

<?xml version="1.0" encoding="UTF-8"?>
<ResponseHeader version="1.0">
    <ResponseCode>T100</ResponseCode>
    <SubmissionIdentifier>1</SubmissionIdentifier>
</ResponseHeader>

<?xml version="1.0" encoding="UTF-8"?>
<SubmissionProgress xmlns="sss"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        
    status="inProgress"
    submissionIdentifier="1"
    submissionType="live">
    <PFile status="rejected"
        index="1"
        pFileIdentifier="999">
        <Exception errorCode="2001" outcomeType="rejectFile">
            <Description>There.file.  </Description>
            <SourceRecord index="3">...</SourceRecord>
        </Exception>
    </PFile>
</SubmissionProgress>
xjc已成功生成

ResponseHeader SubmissionProgress (以及其中的每个元素)类,如果我将此字符串拆分为2个不同的字符串,我可以完全解组这两个类。
但是,如果我把它保存在同一个字符串中并尝试将它传递给两个unmarshallers顺序它会在第一个unmarshall中断。
我正在使用此代码从一个字符串解组:

Reader reader = new StringReader(response);
JAXBContext jcrh = JAXBContext.newInstance(ResponseHeader.class);
JAXBContext jcsp = JAXBContext.newInstance(SubmissionProgress.class);
Unmarshaller urh = jcrh.createUnmarshaller();
Unmarshaller usp = jcsp.createUnmarshaller();
ResponseHeader rh = (ResponseHeader) urh.unmarshal(reader);
SubmissionProgress sr = (SubmissionProgress) usp.unmarshal(reader);

我得到以下异常(在 ResponseHeader rh =(ResponseHeader)urh.unmarshal(reader); ):

uk.co.bacs.submissions.ResponseHeader@fced4
javax.xml.bind.UnmarshalException
 - with linked exception:
[org.xml.sax.SAXParseException: The processing instruction target matching "[xX][mM][lL]" is not allowed.]
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:315)
(...)

在这些情况下是否有一些JAXB调整(一个流中有多个XML文件)?

2 个答案:

答案 0 :(得分:2)

我不知道JAXB的调整;我做这种事情的方式是实现一个XmlEventReader(或XmlStreamReader),它在需要时模拟文档结束。请注意,Unmarshaller.unmarshal()将其中一个作为参数。要确保正确显示事件序列,请观看“正常”文档的事件序列。你会做两个unmarshal()s。

答案 1 :(得分:0)

由于JAXB无法单独读取文件,因此我找到了两种可行的解决方案。

第一个也是更简单的一个,如果流很小,将把它全部读成一个字符串并将其拆分

String xml = "<?xml ... <?xml ...";
String[] xmlArray = xml.split("<\\?xml");
ObjectA a = (ResponseHeader) u.unmarshal(new StringReader("<?xml"+xmlArray[1]);
ObjectB b = (SubmissionProgress) u2.unmarshal(new StringReader("<?xml"+xmlArray[2));

但是,作为练习,为了更清晰的代码以及将来使用更大的流(一次处理一个对象),我制作了MultiXMLDocReader类

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

public class MultiXMLDocReader extends Reader {
    private BufferedReader reader;
    private String buffer;
    private int bufferPos;
    private boolean firstDocument;
    private boolean realEOF;
    private boolean enforceEOF;

    public MultiXMLDocReader(Reader reader) {
        this.reader = new BufferedReader(reader);
        firstDocument = true;
        buffer = "";
        bufferPos = 0;
        realEOF = enforceEOF = false;
    }

    @Override
    public void close() throws IOException {
        enforceEOF = false;
        if (realEOF) reader.close();
    }

    @Override
    public int read() throws IOException {
        char[] buffer = new char[1];
        int result = read(buffer, 0, 1);
        if (result < 0) return -1;
        return buffer[0];
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        if (enforceEOF) return -1;
        int lenLeft = len;
        int read = 0;
        while (lenLeft > 0) {
            if (buffer.length()>0) {
                char[] lbuffer = buffer.toCharArray();
                int bufLen = buffer.length() - bufferPos;
                int newBufferPos = 0;
                if (lenLeft < bufLen) {
                    bufLen = lenLeft;
                    newBufferPos = bufferPos + bufLen;
                }
                else buffer = "";
                System.arraycopy(lbuffer, bufferPos, cbuf, off, bufLen);
                read += bufLen;
                lenLeft -= bufLen;
                off += bufLen;
                bufferPos = newBufferPos;
                continue;
            }
            buffer = reader.readLine();
            if (buffer == null) {
                realEOF = true;
                enforceEOF = true;
                return (read == 0 ? -1 : read);
            }
            else
                buffer += "\n";
            if (buffer.startsWith("<?xml")) {
                if (firstDocument) firstDocument = false;
                else {
                    enforceEOF = true;
                    return (read == 0 ? -1 : read);
                }
            }
        }
        return read;
    }
}

可以像

一样轻松使用
MultiXMLDocReader xmlReader = new MultiXMLDocReader(new InputStreamReader(anyInputStream));
ObjectA a = (ResponseHeader) u.unmarshal(xmlReader);
ObjectB b = (SubmissionProgress) u2.unmarshal(xmlReader);

不将整个流加载到字符串。