Как управлять порядком / последовательностью генерации схемы в памяти JAXB? - PullRequest
2 голосов
/ 13 января 2012

У меня есть 3 xsd-файла, которые зависят друг от друга для построения моих определений элементов. Каждый xsd-файл имеет свое собственное пространство имен. Когда я генерирую свои классы, используя JAXB xjc, я получаю 3 соответствующих пакета. Пока все хорошо.

Моя проблема возникает, когда я хочу выполнить проверку схемы с помощью демаршаллера. Чтобы избежать необходимости читать в файлах xsd, я генерирую схемы на лету из рассматриваемого класса, который не разбирается. Однако, поскольку класс зависит от объектов из 2 других пакетов, он не может генерировать схемы, если я не укажу все 3 пакета. Уже это не очень практичное решение, так как требует, чтобы я заранее знал иерархию объектов / дерево зависимостей и соответственно указывал список пакетов.

Моя большая проблема возникает, когда я пытаюсь создать новую схему из 3 сгенерированных схем, используя SchemaFactory (SchemaFactory.newSchema (Source [])). Очевидно, порядок, в котором схемы предоставляются фабрике схем, имеет решающее значение для разрешения зависимостей. Если первая схема в массиве зависит от определения типа из последнего элемента в массиве, я получаю ошибку разрешения:

org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'ns1:InCalculationDataType' to a(n) 'type definition' component.

Если я изменю порядок и поставлю 3-ю схему первой, она будет выполнена без ошибок.

Это делает почти невозможным написание достаточно общего метода, а скорее приходится кодировать для каждого случая XSD отдельно.

Что я могу сделать, чтобы облегчить эту проблему? Есть ли какой-нибудь способ заставить SchemaFactory сначала прочитать все и только потом генерировать свои ошибки, если он найдет какие-либо? Я знаю, что вы можете создать ErrorHandler, однако JavaDocs указывают, что, если он выдает фатальную ошибку, любая дальнейшая обработка ненадежна.

EDIT

Просто для собственного спокойствия я попытался создать обработчик ошибок, который игнорировал нефатальные ошибки (просто регистрировал их), однако сгенерированная схема была ненадежной и не могла правильно проверить ошибки XML. Следовательно, для меня это не имело значения.

КОНЕЦ РЕДАКТИРОВАНИЯ

Будем благодарны за любые предложения или мысли.

Спасибо!

Эрик

1 Ответ

5 голосов
/ 16 января 2012

После долгих поисков я наконец нашел ответ. Надеюсь, это поможет кому-то еще. В StackOverflow уже есть другие темы, связанные с этой проблемой, но, не зная правильных ключевых слов, я не нашел ответов.

Решением является использование LSResourceResolver для фабрики схем. а именно:

schemaFactory.setResourceResolver(new LSResourceResolver(){})

, где LSResourceResolver () отвечает за возврат ресурса include / import, который требуется XSD.

При поиске LSResourceResolver в SO найдено несколько полезных тем: https://stackoverflow.com/a/3830649/827480, https://stackoverflow.com/a/2342859/827480

Я постараюсь опубликовать свое собственное решение позже, когда у меня будет немного больше времени, но оно близко следует тому, что уже было предложено в двух вышеупомянутых ссылках (мое немного более упрощено с использованием строк вместо потоков ...) .

EDIT

Как и было обещано, вот фрагмент кода, с которым я закончил:

        // 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[]{})));

и метод 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;
}

КОНЕЦ РЕДАКТИРОВАНИЯ

Я надеюсь, что это может помочь кому-то еще в будущем.

Спасибо

Эрик

...