How to control JAXB in-memory schema generation ordering/sequence?

后端 未结 1 819
离开以前
离开以前 2021-01-14 22:15

I\'ve got 3 xsd files that depend on each other to build my element definitions. Each xsd file has its own namespace. When I generate my classes using JAXB xjc, I get 3 co

相关标签:
1条回答
  • 2021-01-14 23:07

    After much searching, I finally found the answer. Hopefully this will help someone else. There are already other threads on StackOverflow relating to this issue, but without knowing the proper keywords, I wasn't finding the responses.

    The solution is to use an LSResourceResolver for the schema factory. ie:

    schemaFactory.setResourceResolver(new LSResourceResolver(){})
    

    where LSResourceResolver() is responsible for returning the include/import resource that is required by the XSD.

    Searching for LSResourceResolver in SO found a few useful threads: https://stackoverflow.com/a/3830649/827480, https://stackoverflow.com/a/2342859/827480

    I will try to post my own solution later when I have a little more time, but it closely follows what was already suggested in the two above links (mine is a little more simplified by using Strings instead of streams...).

    EDIT

    As promised, here is the snippet of code I ended up with:

            // get the schemas used by this class
            final Map<String, String> schemas = new HashMap<String,String>();
            schemas.putAll(generateSchemas(jc));
    
            List<StreamSource> sources = new ArrayList<StreamSource>();
            for( String schema : schemas.values() )
                sources.add( new StreamSource( new ByteArrayInputStream(schema.getBytes())));
    
            SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
            sf.setResourceResolver(new LSResourceResolver() {
                @Override
                public LSInput resolveResource(String type, final String namespaceURI, String publicId, String systemId, String baseURI){
                    logger.debug( "Need to resolve Resource: " + namespaceURI );
                    return new LSInput(){
                        @Override
                        public String getStringData() {
                            // return the schema if found
                            if( schemas.containsKey(namespaceURI)){
                                if( logger.isTraceEnabled())
                                    logger.trace("resourceResolver: Resolving schema for namespace: " + namespaceURI + schemas.get(namespaceURI) );
                                return schemas.get(namespaceURI);
                            }
                            else
                                return null;
                        }
                        @Override
                        public Reader getCharacterStream() {
                            return null;
                        }
                        @Override
                        public void setCharacterStream(Reader paramReader) {
                        }
                        @Override
                        public InputStream getByteStream() {
                            return null;
                        }
                        @Override
                        public void setByteStream(InputStream paramInputStream) {
                        }
                        @Override
                        public void setStringData(String paramString) {
                        }
                        @Override
                        public String getSystemId() {
                            return null;
                        }
                        @Override
                        public void setSystemId(String paramString) {
                        }
                        @Override
                        public String getPublicId() {
                            return null;
                        }
                        @Override
                        public void setPublicId(String paramString) {
                        }
                        @Override
                        public String getBaseURI() {
                            return null;
                        }
                        @Override
                        public void setBaseURI(String paramString) {
                        }
                        @Override
                        public String getEncoding() {
                            return null;
                        }
                        @Override
                        public void setEncoding(String paramString) {
                        }
                        @Override
                        public boolean getCertifiedText() {
                            return false;
                        }
                        @Override
                        public void setCertifiedText(boolean paramBoolean) {
                        }
                    };
                }
            });
    
            // validate the schema
            u.setSchema(sf.newSchema(sources.toArray(new StreamSource[]{})));
    

    and method generateSchemas(jc):

    private Map<String, String> generateSchemas (JAXBContext jaxbContext) throws JAXBException{
        // generate the schemas
        final Map<String, ByteArrayOutputStream> schemaStreams = new LinkedHashMap<String,ByteArrayOutputStream>();
    
        try {
            jaxbContext.generateSchema(new SchemaOutputResolver(){
                @Override
                public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    logger.debug( "GenerateSchemas: adding namespace: " + namespaceUri);
                    schemaStreams.put(namespaceUri, out);
                    StreamResult streamResult = new StreamResult(out);
                    streamResult.setSystemId("");
                    return streamResult;
                }});
        } catch (IOException e) {
            // no IO being performed.  Can safely ignore any IO exception.
        }
    
        // convert to a list of string
        Map<String,String> schemas = new LinkedHashMap<String,String>();
        for( Map.Entry<String, ByteArrayOutputStream> entry : schemaStreams.entrySet() ){
            String schema = entry.getValue().toString();
            String namespace = entry.getKey();
            schemas.put(namespace, schema);
        }
    
        // done
        return schemas;
    }
    

    END EDIT

    I hope this can help someone else in the future.

    Thanks,

    Eric

    0 讨论(0)
提交回复
热议问题