【问题标题】:JAXB ~ Dynamically parse multiple namespacesJAXB ~ 动态解析多个命名空间
【发布时间】:2020-06-04 12:15:02
【问题描述】:

我正在尝试创建一个可用于以下 XML 文件的解组器:

<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd 
  xml:lang="en">
  [...]
</REQ-IF>
<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd" 
  xmlns:configuration="http://eclipse.org/rmf/pror/toolextensions/1.0"
  xmlns:id="http://pror.org/presentation/id"
  xmlns:xhtml="http://www.w3.org/1999/xhtml">
  [...]
</REQ-IF>
<?xml version="1.0" encoding="UTF-8"?>
  <REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:doors="http://www.ibm.com/rdm/doors/REQIF/xmlns/1.0"
  xmlns:reqif="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:reqif-common="http://www.prostep.org/reqif"
  xmlns:reqif-xhtml="http://www.w3.org/1999/xhtml"
  xmlns:rm="http://www.ibm.com/rm"
  xmlns:rm-reqif="http://www.ibm.com/rm/reqif"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  [...]
</REQ-IF>

所有这些文件在结构上都是相同的,并且基于相同的顶级命名空间,但还包含各种可变的子级命名空间和其他“事物”(根据我的理解应该是属性,但不是) ,需要保存在系统中。

到目前为止,我已经设法节省了这么多:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd">
  [...]
</REQ-IF>

但是,我的预期结果是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd 
  xml:lang="en">
  [...]
</REQ-IF>

所以顶级命名空间被保存了,但是子级命名空间和其他“东西”在导入/导出过程中丢失了。这很糟糕。

考虑到它们是动态生成的,我如何保存其他子命名空间和其他“事物”?

基本上,我想说的是“在解析 XML 时以任何你喜欢的方式保存所有这些额外的属性,一旦你再次导出 XML,就可以完全按照原来的方式重新编写它们”。

【问题讨论】:

  • 所以您的用例是使用 JAXB 读取 XML,使用它执行一些工作,然后以与开始时相同的方式将其序列化回来?在处理过程中传入消息的结构是否应该改变?
  • @Daniil 实际上,我在这里处理的是非常复杂的 ReqIF 文档。文件本身的结构应该是一样的,但我希望能够在文件结构中更深层次地赋予属性值。根元素及其属性应保持不变。
  • 将例如这个 SO 答案stackoverflow.com/a/7736235/2792888 中提出的解决方案就足够了,或者您需要为每个具体的 XML 动态保存命名空间声明?
  • JAXB 不这样做。您需要更好地控制 XML 解释/生成。考虑 JDOM 2
  • @Danill :我已经检查了那个答案,遗憾的是它不满足我的问题的要求。正如您所说,我需要能够动态读出每个 XML 的名称空间声明。另外,我确实有一个带有namespace="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd", xmlns={ @XmlNs(prefix="xsi", namespaceURI="http://www.w3.org/2001/XMLSchema-instance"), } 的package-info.java。然而,即使有那个文件(没有它,我得到一个unexpected element-error),xmlns:xsi-namespace 也不会被解析。

标签: java xml jaxb reqif


【解决方案1】:

如果您的主要用例是读取 ReqIF 文件,请考虑在 https://www.eclipse.org/rmf/ 处有一个 ReqIF (de)serializer 的开源实现

【讨论】:

    【解决方案2】:

    不幸的是,仅 JAXB 似乎无法动态管理所有命名空间前缀,您需要将其与另一种解析机制结合使用。

    我会尝试实现这样的东西(只是粗略的实现,细节如下):

    public class MyXmlHandler {
    
        XMLInputFactory xif = XMLInputFactory.newInstance();
    
        XMLOutputFactory xof = XMLOutputFactory.newInstance();
    
        XMLEventFactory xef = XMLEventFactory.newInstance();
    
        /**
         * Retrieve XMLEvent for root element
         */
        public StartElement getStartElement(String source) throws XMLStreamException {
            XMLEvent event;
            XMLEventReader reader = xif.createXMLEventReader(new StringReader(source));
            while (reader.hasNext()) {
                event = reader.nextEvent();
                if (event.isStartElement()) {
                    return event.asStartElement();
                }
                // alternativery you can retrieve here also QNames for first level child elements 
                // and return all this data in some synthetic wrapper class
            }
            return null; // alternatively throw an exception
        }
    
        /**
         * Write root element, than some content from JAXB elements, than end element
         */
        public void write(
            Marshaller marshaller, 
            Writer writer, 
            StartElement root, 
            List<JAXBElement> elements
        ) throws JAXBException, XMLStreamException {
            XMLEventWriter xew = xof.createXMLEventWriter(writer);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
            xew.add(root);
            for(JAXBElement element : elements) {
                marshaller.marshal(element, xew);
            }
            xew.add(xef.createEndElement(root.getName(), root.getNamespaces()));
            xew.close();
        }
    
    }
    

    并像这样使用它:

    // create JAXB context and unmarshaller
    JAXBContext ctx = JAXBContext.newInstance(RootClass.class);
    Unmarshaller unmarshaller = ctx.createUnmarshaller();
    
    // unmarshall XML
    JAXBElement<RootClass> element = unmarshaller.unmarshal(source, RootClass.class);
    RootClass rootValue = element.getValue();
    
    // extract root element data from XML
    StartElement root = handler.getStartElement(data);
    
    // perform some business logic
    
    // create marshaller
    Marshaller marshaller = ctx.createMarshaller();
    
    // create list of JAXBElements for root children
    List<JAXBElement> elements = new ArrayList<>();
    QName qname = ... // construct qualified name or retrieve it from saved structure
    // very schematic, names of the child elements depend on your implementation
    elements.add(new JAXBElement(qname , rootValue.getChild().getClass(), rootValue.getChild()));
    handler.write(marshaller, writer, root, elements);
    

    在保留根元素数据时,您还可以将其子元素的 QNames 保存在某个包装类中。到目前为止,我看到 REQ-IF 结构包含标题、核心内容和工具扩展。您可以为所有这些保存 QName,然后在编组过程中使用它们来构造 JAXB 元素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-13
      • 1970-01-01
      • 2015-06-21
      • 2021-10-21
      • 1970-01-01
      • 2022-10-14
      • 2012-08-15
      • 2014-11-07
      相关资源
      最近更新 更多