使用模式按照模式重新排序XML文档的元素

假设我有一个XML文档(表示为文本,W3C DOM,无论如何),还有一个XML Schema。 XML文档具有模式定义的所有正确元素,但顺序错误。

如何使用模式“重新排序”文档中的元素以符合模式定义的顺序?

我知道这应该是可能的,可能使用XSOM ,因为JAXB XJC代码生成器使用元素的正确序列化顺序来注释其生成的类。

但是,我不熟悉XSOM API,它非常密集,所以我希望你们中的一个人对它有一些经验,并且可以指出我正确的方向。 像“在这个父元素中允许哪些子元素,以什么顺序?”之类的东西。


让我举个例子。

我有一个像这样的XML文档:

    

我有一个XML Schema,它说的内容必须是然后是 。 现在很清楚,如果我尝试根据模式validation文档,它会失败,因为的顺序错误。 但我知道我的文档提前是“错误的”,所以我还没有使用模式进行validation。 但是,我知道我的文档具有模式定义的所有正确元素,只是顺序错误。

我想要做的是以编程方式检查Schema(可能使用XSOM – 这是XML Schema的对象模型),并询问它应该是什么内容 。 API将公开“您需要后跟 ”的信息。

所以我使用我的XML文档(使用DOM API)并相应地重新安排,以便现在文档将根据模式进行validation。

了解XSOM在这里很重要 – 它是一个java API,它表示XML Schema中包含的信息, 而不是我的实例文档中包含的信息。

我不想做的是从架构生成代码,因为架构在构建时是未知的。 此外,XSLT没有用,因为元素的正确排序仅由模式中包含的数据字典决定。

希望现在已经足够明确了。

您的问题转化为:您有一个与架构不匹配的XSM文件,并且您希望将其转换为有效的内容。

使用XSOM,您可以在XSD中读取结构并可能分析XML,但仍需要从无效表单到有效表单的其他映射。 使用样式表会更容易,因为您将遍历XML,使用XPath节点以正确的顺序处理元素。 使用XML在梨之前需要苹果,样式表将首先复制苹果节点(/ Fruit / Apple),然后复制pear节点。 这样,无论旧文件中的顺序如何,它们在新文件中的顺序都是正确的。

您可以使用XSOM执行的操作是读取XSD并生成将重新排序数据的样式表。 然后使用该样式表转换XML。 一旦XSOM为XSD生成了样式表,您就可以重新使用样式表,直到修改XSD或需要其他XSD。

当然,您可以使用XSOM以正确的顺序立即复制节点。 但是,由于这意味着您的代码必须遍历所有节点和子节点,因此可能需要一些时间来完成处理。 样式表也会这样做,但变换器将能够更快地处理它。 它可以直接处理数据,而Java代码必须通过XMLDocument属性获取/设置每个节点。


因此,我将使用XSOM为XSD生成样式表,该样式表只是逐个节点地复制XML节点以便一次又一次地重复使用。 只有在XSD更改时才需要重写样式表,并且它的执行速度比Java API需要遍历节点本身时要快。 样式表不关心订单,因此它总是以正确的顺序结束。


为了使它更有趣,您可以跳过XSOM并尝试使用读取XSD的样式表来从中生成另一个样式表。 生成的样式表将按照样式表中定义的确切顺序复制XML节点。 它会很复杂吗? 实际上,样式表需要为每个元素生成模板,并确保以正确的顺序处理此元素中的子元素。

当我想到这一点时,我想知道这是否已经完成。 它非常通用,几乎可以处理每个XSD / XML。

让我们看看……使用“// xsd:element / @ name”,您将获得模式中的所有元素名称。 每个唯一名称都需要转换为模板。 在这些模板中,您需要处理特定元素的子节点,这稍微复杂一些。 元素可以有一个引用,您需要遵循它。 否则,获取所有子xsd:element节点。

我对此还没有一个好的答案,但我必须指出那里有可能存在歧义。 考虑这个架构:

                   

这个输入XML:

        

这可以通过重新排序 ,或通过重新排序来符合模式。 似乎没有任何理由偏爱一个而不是另一个。

大约两个星期我遇到了同样的问题。 最后我获得了突破。 这可以使用JAXB编组/解组function来实现。

在JAXB marshal / unmarshal中,XMLvalidation是一项可选function。 因此,在创建Marshaller和UnMarshaller对象时,我们不会调用setSchema(schema)方法。 省略此步骤可避免marshal / unmarshal的XMLvalidationfunction。

所以现在,

  1. 如果XML中不存在XSD中的任何必需元素,则会被忽略。
  2. 如果XML中不存在XSD中不存在的任何标记,则不会抛出任何错误,并且在编组/解组后得到的新XML中不存在该错误。
  3. 如果元素不按顺序排列,则重新排序。 这是由JAXB生成的POJO完成的,我们在创建JAXBContext时传递这些POJO。
  4. 如果某个元素在其他标记内部放错位置,则在新XML中将其省略。 编组/解组时不会抛出任何错误。

 public class JAXBSequenceUtil { public static void main(String[] args) throws JAXBException, IOException { String xml = FileUtils.readFileToString(new File( "./conf/out/Response_103_1015700001&^&IOF.xml")); System.out.println("Before marshalling : \n" + xml); String sequencedXml = correctSequence(xml, "org.acord.standards.life._2"); System.out.println("After marshalling : \n" + sequencedXml); } /** * @param xml * - XML string to be corrected for sequence. * @param jaxbPackage * - package containing JAXB generated classes using XSD. * @return String - xml with corrected sequence * @throws JAXBException */ public static String correctSequence(String xml, String jaxbPackage) throws JAXBException { JAXBContext jaxbContext = JAXBContext.newInstance(jaxbPackage); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); Object txLifeType = unmarshaller.unmarshal(new InputSource( new StringReader(xml))); System.out.println(txLifeType); StringWriter stringWriter = new StringWriter(); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.marshal(txLifeType, stringWriter); return stringWriter.toString(); } } 

基本上,您希望获取根元素,然后以递归方式查看文档中的子项以及模式中定义的子项并使顺序匹配。

我会给你一个C#-syntax解决方案,因为那是我日夜编写的代码,它非常接近Java。 请注意,我不得不对XSOM进行猜测,因为我不知道它的API。 我也已经编写了XML Dom方法,因为给你的C#可能不会有帮助:)

//假设第一个调用是SortChildrenIntoNewDocument(sourceDom.DocumentElement,targetDom.DocumentElement,schema.RootElement)

 public void SortChildrenIntoNewDocument( XmlElement source, XmlElement target, SchemaElement schemaElement ) { // whatever method you use to ask the XSOM to tell you the correct contents SchemaElement[] orderedChildren = schemaElement.GetChildren(); for( int i = 0; i < orderedChildren.Length; i++ ) { XmlElement sourceChild = source.SelectChildByName( orderedChildren[ i ].Name ); XmlElement targetChild = target.AddChild( sourceChild ) // recursive-call SortChildrenIntoNewDocument( sourceChild, targetChild, orderedChildren[ i ] ); } } 

如果它是一个深树,我不会推荐一个递归方法,在这种情况下你必须创建一些'tree walker'类型的对象。 这种方法的优点是你将能够处理更复杂的事情,比如模式说你可以拥有0或更多的元素,你可以继续处理源节点,直到不再匹配,然后移动模式walker从那里开始。