2012-01-12 95 views
2

我有3个互相依赖的xsd文件来构建我的元素定义。每个xsd文件都有自己的名称空间。当我使用JAXB xjc生成我的类时,我得到3个相应的包。到现在为止还挺好。如何控制JAXB内存架构生成排序/序列?

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

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

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

如果我修改的顺序,先放3架构,它成功没有错误。

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

有什么我可以做的,以缓解这个问题?有没有办法强制SchemaFactory首先读取所有内容,并且只有在发现任何错误时才会产生错误?我知道你可以创建一个ErrorHandler,但是JavaDocs指出如果它抛出一个致命错误,任何进一步的处理都是不可靠的。

编辑

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

编辑完

任何建议或想法,将不胜感激。

谢谢!

埃里克

+0

我记得在我的经验,我有一个案子时'SchemaFactory.newSchema模式的顺序()'事做。如果记得没错,那正是你得到的:一旦架构取决于其他。但是如果你想立即生成模式,你知道它们之间的依赖关系吗? – 2012-01-13 22:59:46

+0

是的,我确实知道它们的依赖关系,但考虑到我想要在运行时生成几个不同的xsd,我希望使用通用的一段代码来实例化所需的class/pkg中的JAXBContext并生成模式根据其需求。但是,如果我需要手动编写依赖关系,那么每个XSD代必须独立编码,这是一种难看的解决方案,因为我有几个这样的XSD /对象可以生成。 – 2012-01-14 02:26:37

回答

5

经过多次搜索,我终于找到答案。希望这会帮助别人。 StackOverflow上已经有与此问题有关的其他线程,但不知道适当的关键字,我没有找到答案。

解决方案是为模式工厂使用LSResourceResolver。即:

schemaFactory.setResourceResolver(new LSResourceResolver(){}) 

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

在SO搜索LSResourceResolver的发现一些有用的主题:https://stackoverflow.com/a/3830649/827480https://stackoverflow.com/a/2342859/827480

我会尝试发布自己的解决方案后,当我有更多一点的时间,但它紧随什么两个已经建议上面的链接(通过使用字符串而不是流来简化)。

编辑

正如所承诺的,这里是我结束了的代码片段:

 // 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; 
} 

编辑完

我希望这可以帮助未来的其他人。

感谢,

埃里克