如何使用JAXB从一个Java模型生成多个略有不同的XSD架构?

时间:2014-10-02 07:40:23

标签: java xml jaxb xsd

我有一组相关的Java类,它们能够保存我需要的数据。下面是我所拥有的简化类图:

enter image description here

现在我需要从XML导入数据,为此我想生成XSD架构。问题是我想要几个这样的XSD架构:

  1. 允许导入整个数据图的文件。
  2. 仅允许RootNote.fieldA和ChildNodeA。
  3. 仅允许RootNote.fieldB和ChildNodeB。
  4. 我可以使用JAXB(以编程方式)轻松生成满足nr.1要求的XSD。但对于相同类的情况nr.2和nr.3,有没有办法做到这一点?换句话说,我似乎需要像" profiles"在JAXB。

    更新

    以下是我生成XSD架构的方法:

    JAXBContext jc = JAXBContext.newInstance(RootNode.class);
    
    final File baseDir = new File(".");
    
    class MySchemaOutputResolver extends SchemaOutputResolver {
      public Result createOutput( String namespaceUri, String suggestedFileName ) throws IOException {
        return new StreamResult(new File(baseDir,suggestedFileName));
      }
    }
    
    jc.generateSchema(new MySchemaOutputResolver());
    

2 个答案:

答案 0 :(得分:0)

这不是一个完整的答案,只是一个想法。

您可能使用javax.xml.bind.JAXBContext.generateSchema(SchemaOutputResolver)方法生成架构,因此您基本上使用特定的JAXBContext实例。此实例基于类中的注释构建。在构建上下文时,这些注释被组织成一个模型,然后用于所有操作。

因此,要生成不同的模式,您可能需要创建不同的上下文。您无法更改每个案例的注释,但您可以通过不同方式读取注释。

看看AnnotationReader。这就是JAXB RI在幕后使用从Java类加载注释的方法。您可以创建自己的实现并在创建JAXBContext时使用它。这是一个example of something similar

final AnnotationReader<Type, Class, Field, Method> annotationReader = new AnnoxAnnotationReader();

final Map<String, Object> properties = new HashMap<String, Object>();

properties.put(JAXBRIContext.ANNOTATION_READER, annotationReader);

final JAXBContext context = JAXBContext.newInstance(
    "org.jvnet.annox.samples.po",
    Thread.currentThread().getContextClassLoader(),
    properties);

那么如何编写自己的注释阅读器,哪个会考虑你所谓的“个人资料”?您可以创建自己的注释@XmlSchemaProfile(name="foo")。然后,您的注释阅读器将检查此注释是否与所需值一起存在,然后返回或忽略它。您将能够从同一Java模型构建不同的上下文 - 并因此根据您的@XmlSchemaProfile注释定义的配置文件生成不同的模式。

答案 1 :(得分:0)

我找到了适合我的解决方案。我们的想法是将XSD生成的结果输出到XML文档(内存中的DOM)中。 JAXB允许这样做。在此之后,您可以按照您希望的方式操作文档,添加或删除部件。

我写了一些过滤器,白名单或黑名单字段(在XSD中它们是元素)和类(在XSD中它们是复杂类型)。虽然我发现这种方法存在许多潜在的问题,但它在我的案例中完成了这项工作。以下是案例2架构的代码:

// This SchemaOutputResolver implementation saves XSD into DOM
static class DOMResultSchemaOutputResolver extends SchemaOutputResolver  {

    private List<DOMResult> results = new LinkedList<DOMResult>();

    @Override
    public Result createOutput(String ns, String file) throws IOException {
      DOMResult result = new DOMResult();
      result.setSystemId(file);
      results.add(result);
      return result;
    }

    public Document getDocument() {
      return (Document)results.get(0).getNode();
    }

    public String getFilename() {
      return results.get(0).getSystemId();
    }

}

  // This method serializes the DOM into file
  protected void serializeXsdToFile(Document xsdDocument, String filename) throws IOException {
    OutputFormat format = new OutputFormat(xsdDocument);
    format.setIndenting(true);
    FileOutputStream os = new FileOutputStream(filename);
    XMLSerializer serializer = new XMLSerializer(os, format);
    serializer.serialize(xsdDocument);
  }

  @Test
  public void generateSchema2() throws JAXBException, IOException, XPathExpressionException {
    JAXBContext context = JAXBContext.newInstance(RootNode.class);
    DOMResultSchemaOutputResolver schemaOutputResolver = new DOMResultSchemaOutputResolver();

    context.generateSchema(schemaOutputResolver);

    // Do your manipulations here as you want. Below is just an example!
    filterXsdDocumentComplexTypes(schemaOutputResolver.getDocument(), asList("childNodeA"), true);
    filterXsdDocumentElements(schemaOutputResolver.getDocument(), asList("fieldB"));

    serializeXsdToFile(schemaOutputResolver.getDocument(), "xf.xsd");
  }

  private boolean shouldComplexTypeBeDeleted(String complexTypeName, List<String> complexTypes, boolean whitelist) {
    return (whitelist && !complexTypes.contains(complexTypeName)) || (!whitelist && complexTypes.contains(complexTypeName));
  }


  protected void filterXsdDocumentComplexTypes(Document xsdDocument, List<String> complexTypes, boolean whitelist) throws XPathExpressionException {
    XPath xPath = XPathFactory.newInstance().newXPath();
    NodeList complexTypeNodes = (NodeList)xPath.evaluate("//*[local-name() = 'complexType']", xsdDocument, XPathConstants.NODESET);
    for (int i = 0; i < complexTypeNodes.getLength(); i++) {
      Node node = complexTypeNodes.item(i);
      Node complexTypeNameNode = node.getAttributes().getNamedItem("name");

      if (complexTypeNameNode != null) {
        if (shouldComplexTypeBeDeleted(complexTypeNameNode.getNodeValue(), complexTypes, whitelist)) {
          node.getParentNode().removeChild(node);
        }
      }
    }

    NodeList elements = (NodeList)xPath.evaluate("//*[local-name() = 'element']", xsdDocument, XPathConstants.NODESET);
    for (int i = 0; i < elements.getLength(); i++) {
      Node node = elements.item(i);
      Node typeNameNode = node.getAttributes().getNamedItem("type");
      if (typeNameNode != null) {
        if (shouldComplexTypeBeDeleted(typeNameNode.getNodeValue(), complexTypes, whitelist) && !typeNameNode.getNodeValue().startsWith("xs")) {
          node.getParentNode().removeChild(node);
        }
      }
    }
  }

  protected void filterXsdDocumentElements(Document xsdDocument, List<String> blacklistedElements) throws XPathExpressionException {
    XPath xPath = XPathFactory.newInstance().newXPath();

    NodeList elements = (NodeList)xPath.evaluate("//*[local-name() = 'element']", xsdDocument, XPathConstants.NODESET);
    for (int i = 0; i < elements.getLength(); i++) {
      Node node = elements.item(i);
      if (blacklistedElements.contains(node.getAttributes().getNamedItem("name").getNodeValue())) {
        node.getParentNode().removeChild(node);
      }
    }
  }