代码之家  ›  专栏  ›  技术社区  ›  bajafresh4life

使用JAXB和Stax封送XML文档的验证

  •  8
  • bajafresh4life  · 技术社区  · 16 年前

    我创建了一个XML模式(foo.xsd)并使用 xjc 为JAXB创建绑定类。假设根元素是 collection 我在写N document

    收集 根元素和JAXB来封送文档子树 Marshaller.marshal(JAXBElement, XMLEventWriter) jaxb's unofficial user's guide .

    我的问题是,如何在封送XML时验证它?如果我将一个模式绑定到JAXB marshaller(使用 Marshaller.setSchema() ),我得到验证错误,因为我只是编组一个子树(它抱怨它没有看到 Stax XMLEventWriter 或者类似的。

    JAXB 为了在不耗尽内存的情况下封送和解封大型XML文档,如果有更好的方法,请告诉我。

    1 回复  |  直到 13 年前
        1
  •  3
  •   Community Mohan Dere    9 年前

    一些Stax实现似乎能够验证输出。类似问题的答案如下:

    Using Stax2 with Woodstox

        2
  •  1
  •   basin    5 年前

    您可以使根集合变懒,并仅在封送处理程序调用时实例化项 Iterator.next() . 然后打个电话给 marshal() 将产生一个巨大的经过验证的XML。不会耗尽内存,因为已经序列化的bean是由GC收集的。

    null 作为一个集合元素,如果需要有条件地跳过它。不会有NPE。

    xmlschema验证器本身似乎消耗很少的内存,即使在大型XML上也是如此。

    ArrayElementProperty.serializeListBody()

    import java.io.IOException;
    import java.io.StringReader;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.util.AbstractList;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.xml.XMLConstants;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.SchemaOutputResolver;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAnyElement;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.namespace.QName;
    import javax.xml.transform.Result;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.validation.Schema;
    import javax.xml.validation.SchemaFactory;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement(name = "TestHuge")
    public class TestHuge {
    
        static final boolean MISPLACE_HEADER = true;
    
        private static final int LIST_SIZE = 20000;
    
        static final String HEADER = "Header";
    
        static final String DATA = "Data";
    
        @XmlElement(name = HEADER)
        String header;
    
        @XmlElement(name = DATA)
        List<String> data;
    
        @XmlAnyElement
        List<Object> content;
    
        public static void main(final String[] args) throws Exception {
    
            final JAXBContext jaxbContext = JAXBContext.newInstance(TestHuge.class);
    
            final Schema schema = genSchema(jaxbContext);
    
            final Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.setSchema(schema);
    
            final TestHuge instance = new TestHuge();
    
            instance.content = new AbstractList<Object>() {
    
                @Override
                public Object get(final int index) {
                    return instance.createChild(index);
                }
    
                @Override
                public int size() {
                    return LIST_SIZE;
                }
            };
    
            // throws MarshalException ... Invalid content was found starting with element 'Header'
            marshaller.marshal(instance, new Writer() {
    
                @Override
                public void write(final char[] cbuf, final int off, final int len) throws IOException {}
    
                @Override
                public void write(final int c) throws IOException {}
    
                @Override
                public void flush() throws IOException {}
    
                @Override
                public void close() throws IOException {}
            });
    
        }
    
        private JAXBElement<String> createChild(final int index) {
            if (index % 1000 == 0) {
                System.out.println("serialized so far: " + index);
            }
            final String tag = index == getHeaderIndex(content) ? HEADER : DATA;
    
            final String bigStr = new String(new char[1000000]);
            return new JAXBElement<String>(new QName(tag), String.class, bigStr);
        }
    
        private static int getHeaderIndex(final List<?> list) {
            return MISPLACE_HEADER ? list.size() - 1 : 0;
        }
    
        private static Schema genSchema(final JAXBContext jc) throws Exception {
            final List<StringWriter> outs = new ArrayList<>();
            jc.generateSchema(new SchemaOutputResolver() {
    
                @Override
                public Result createOutput(final String namespaceUri, final String suggestedFileName)
                                                                                                      throws IOException {
                    final StringWriter out = new StringWriter();
                    outs.add(out);
                    final StreamResult streamResult = new StreamResult(out);
                    streamResult.setSystemId("");
                    return streamResult;
                }
            });
            final StreamSource[] sources = new StreamSource[outs.size()];
            for (int i = 0; i < outs.size(); i++) {
                final StringWriter out = outs.get(i);
                sources[i] = new StreamSource(new StringReader(out.toString()));
            }
            final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            final Schema schema = sf.newSchema(sources);
            return schema;
        }
    }