假设我有一个XML文档(表示为文本,W3C DOM,等等),还有一个XML Schema。 XML文档具有模式定义的所有正确元素,但顺序错误。
如何使用架构“重新排序”文档中的元素以符合架构定义的顺序?
我知道这应该是可能的,可能使用XSOM,因为JAXB XJC代码生成器使用元素的正确序列化顺序来注释其生成的类。
但是,我不熟悉XSOM API,它非常密集,所以我希望你们中的一个人有一些经验,可以指出我正确的方向。类似“在这个父元素中允许哪些子元素,以及以什么顺序?”
让我举个例子。
我有一个这样的XML文档:
<A>
<Y/>
<X/>
</A>
我有一个XML Schema,其中<A>
的内容必须是<X>
后跟<Y>
。现在很清楚,如果我尝试根据模式验证文档,它会失败,因为<X>
和<Y>
的顺序错误。但我知道我的文档提前是“错误的”,所以我还没有使用模式进行验证。但是,我做知道我的文档具有模式定义的所有正确元素,只是顺序错误。
我想要的是以编程方式检查Schema(可能使用XSOM - 这是XML Schema的对象模型),并询问它<A>
的内容应该是什么。 API将公开“您需要<X>
后跟<Y>
”的信息。
所以我使用我的XML文档(使用DOM API)并相应地重新安排,以便现在文档将根据模式进行验证。
了解XSOM在这里很重要 - 它是一个java API,它代表XML Schema中包含的信息,不我的实例文档中包含的信息。
我不想做的是从架构生成代码,因为架构在构建时是未知的。此外,XSLT没有用,因为元素的正确排序仅由模式中包含的数据字典决定。
希望现在已经足够明确了。
答案 0 :(得分:3)
我对此还没有一个好的答案,但我必须指出那里有可能存在歧义。考虑这个架构:
<xs:element name="root">
<xs:choice>
<xs:sequence>
<xs:element name="foo"/>
<xs:element name="bar">
<xs:element name="dee">
<xs:element name="dum">
</xs:element>
</xs:sequence>
<xs:sequence>
<xs:element name="bar">
<xs:element name="dum">
<xs:element name="dee">
</xs:element>
<xs:element name="foo"/>
</xs:sequence>
</xs:choice>
</xs:element>
和这个输入XML:
<root>
<foo/>
<bar>
<dum/>
<dee/>
</bar>
</root>
通过重新排序<foo>
和<bar>
,或重新排序<dee>
和<dum>
,可以使其符合架构。似乎没有任何理由偏爱一个而不是另一个。
答案 1 :(得分:3)
我在两周左右遇到了同样的问题。 最后我获得了突破。 这可以使用JAXB编组/解组功能来实现。
在JAXB marshal / unmarshal中,XML验证是一项可选功能。 因此,在创建Marshaller和UnMarshaller对象时,我们不会调用setSchema(schema)方法。 省略此步骤可避免marshal / unmarshal的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();
}
}
答案 2 :(得分:2)
您的问题转化为:您有一个与架构不匹配的XSM文件,并且您希望将其转换为有效的内容。
使用XSOM,您可以读取XSD中的结构并可能分析XML,但仍需要从无效表单到有效表单的其他映射。使用样式表会更容易,因为您将遍历XML,使用XPath节点以正确的顺序处理元素。使用XML在梨之前需要苹果,样式表将首先复制苹果节点(/ Fruit / Apple),然后复制pear节点。这样,无论旧文件中的顺序如何,它们在新文件中的顺序都是正确的。
使用XSOM可以做的是读取XSD并生成将重新排序数据的样式表。然后使用该样式表转换XML。一旦XSOM为XSD生成了样式表,您就可以重新使用样式表,直到修改XSD或需要其他XSD。
当然,您可以使用XSOM以正确的顺序立即复制节点。但由于这意味着您的代码必须遍历所有节点和子节点,因此处理完成可能需要一些时间。样式表也会这样做,但变换器将能够更快地处理它。它可以直接处理数据,而Java代码必须通过XMLDocument属性获取/设置每个节点。
当我想到这一点时,我想知道这是否已经完成。它非常通用,几乎可以处理每个XSD / XML。
让我们看看......使用“// xsd:element / @ name”,您将获得架构中的所有元素名称。每个唯一名称都需要转换为模板。在这些模板中,您需要处理特定元素的子节点,这稍微复杂一些。元素可以有一个参考,您需要遵循。否则,获取所有子xsd:element节点。
答案 3 :(得分:1)
基本上你想要获取根元素,然后以递归方式查看文档中的子项和模式中定义的子项,并使顺序匹配。
我会给你一个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从那里开始。