如何控制JAXB内存中的模式生成顺序/顺序? [英] How to control JAXB in-memory schema generation ordering/sequence?

查看:421
本文介绍了如何控制JAXB内存中的模式生成顺序/顺序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有3个相互依赖的xsd文件来构建我的元素定义。每个xsd文件都有自己的命名空间。当我使用JAXB xjc生成我的类时,我得到3个相应的包。到目前为止一直很好。



当我想用unmarshaller进行模式验证时,我的问题出现了。为了避免必须读入xsd文件,我从有问题的类中动态生成模式是未编组的。但是,由于该类依赖于来自其他2个包的对象,因此除非我指定所有3个包,否则它无法生成模式。这已经不是一个非常实用的解决方案,因为它要求我提前知道对象层次结构/依赖关系树,并相应地指定包列表。



My当我尝试使用SchemaFactory(SchemaFactory.newSchema(Source []))从3个生成的模式创建新模式时,会出现更大的问题。显然,模式提供给模式工厂的顺序对于解析依赖关系至关重要。如果数组中的第一个模式依赖于数组中最后一个元素的类型定义,我会得到一个解决错误:

  org.xml.sax.SAXParseException:src-resolve:无法将名称'ns1:InCalculationDataType'解析为(n)'类型定义'组件。 

如果我修改订单,并将第3个架构放在第一位,它就会顺利成功。



这使得几乎不可能编写一个相当通用的方法,而是必须单独编写每个XSD案例的代码。



有什么办法可以解决这个问题吗?是否有某种方法可以强制SchemaFactory首先读取所有内容,然后只有在找到任何内容时才会生成错误?我知道你可以创建一个ErrorHandler,但是JavaDocs表明如果它抛出致命错误,任何进一步的处理都是不可靠的。



EDIT



为了我自己的安心,我试图创建一个错误处理程序,忽略非致命错误(只记录它们),但是生成的模式不可靠且无法正确验证xml错误。因此,它对我没有价值。



END EDIT



任何建议或者我会很感激。



谢谢!



Eric

解决方案

经过多次搜索,我终于找到了答案。希望这会帮助别人。 StackOverflow上已经存在与此问题相关的其他线程,但在不知道正确的关键字的情况下,我没有找到响应。



解决方案是使用LSResourceResolver架构工厂。 ie:

  schemaFactory.setResourceResolver(new LSResourceResolver(){})

其中LSResourceResolver()负责返回XSD所需的包含/导入资源。



在SO中搜索LSResourceResolver找到了一些有用的线程: https://stackoverflow.com/a/3830649/827480 https://stackoverflow.com/a/2342859/827480



<当我有更多的时间后,我会尝试发布我自己的解决方案,但它会严格遵循上面两个链接中已经提出的建议(通过使用字符串而不是流来简化我的解决方案......)。



编辑



正如所承诺的,这里是我最终的代码片段with:

  //获取此类使用的模式
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(需要解析资源:+ namespaceURI);
返回新的LSInput(){
@Override
public String getStringData(){
//如果找到则返回架构
if(schemas.containsKey(namespaceURI)){
if(logger.isTraceEnabled())
logger.trace(resourceResolver:解析命名空间的模式:+ namespaceURI + schemas.get( namespaceURI));
返回schemas.get(namespaceURI);
}
else
返回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){
}
};
}
});

//验证架构
u.setSchema(sf.newSchema(sources.toArray(new StreamSource [] {})));

和方法generateSchemas(jc):

 私有地图< String,String> generateSchemas(JAXBContext jaxbContext)抛出JAXBException {
//生成模式
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:added namespace:+ namespaceUri);
schemaStreams.put(namespaceUri,out);
StreamResult streamResult = new StreamResult( out);
streamResult.setSystemId();
return streamResult;
}});
} catch(IOException e){
//没有执行IO。可以安全地忽略任何IO异常。
}

//转换为字符串
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);
}

//完成
返回模式;
}

END EDIT



我希望将来可以帮助其他人。



谢谢,



Eric


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 corresponding packages. So far so good.

My problem comes when I want to do schema validation with the unmarshaller. In order to avoid having to read in the xsd files, I generate the schemas on the fly from the class in question being unmarshalled. However, since the class depends on objects from 2 other packages, it is unable to generate the schemas unless I specify all 3 packages. Already, that isn't a very practical solution, as it requires me to know ahead of time the object hierarchy/dependency tree, and specify the package list accordingly.

My bigger problem comes when I try to create a new schema from the 3 generated schemas using the SchemaFactory (SchemaFactory.newSchema(Source[])). Apparently, the order in which the schemas are provided to the schema factory is critical for it to resolve dependencies. If the first schema in the array depends on a type definition from the last element in the array, I get a resolve error:

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

If I modify the order, and put the 3rd schema first, it succeeds without error.

This makes it nearly impossible to write a method fairly generic, but rather have to code for each XSD case individually.

Is there anything I can do to alleviate this problem? Is there some way to force the SchemaFactory to read everything first and only then generate its errors if it finds any? I know you can create an ErrorHandler, however the JavaDocs indicate that if it throws a Fatal error, any further processing is unreliable.

EDIT

Just for my own peace of mind, I tried to create an error handler which ignored non-fatal errors (just logged them), however the generated schema was unreliable and was unable to properly validate xml errors. Consequently, it had no value to me.

END EDIT

Any suggestions or thoughts would be appreciated.

Thanks!

Eric

解决方案

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

这篇关于如何控制JAXB内存中的模式生成顺序/顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆