Java 使用模式按照模式对XML文档的元素重新排序
假设我有一个XML文档(表示为文本、W3CDOM等),还有一个XML模式。XML文档具有模式定义的所有正确元素,但顺序错误 如何使用模式对文档中的元素“重新排序”,以符合模式定义的顺序 我知道这应该是可能的,可能是使用,因为JAXB XJC代码生成器使用元素的正确序列化顺序注释其生成的类 但是,我不熟悉XSOMAPI,而且它非常密集,所以我希望你们中的一位对此有一些经验,并能为我指明正确的方向。类似于“此父元素中允许哪些子元素,以及以什么顺序?”Java 使用模式按照模式对XML文档的元素重新排序,java,xml,xsd,xsom,Java,Xml,Xsd,Xsom,假设我有一个XML文档(表示为文本、W3CDOM等),还有一个XML模式。XML文档具有模式定义的所有正确元素,但顺序错误 如何使用模式对文档中的元素“重新排序”,以符合模式定义的顺序 我知道这应该是可能的,可能是使用,因为JAXB XJC代码生成器使用元素的正确序列化顺序注释其生成的类 但是,我不熟悉XSOMAPI,而且它非常密集,所以我希望你们中的一位对此有一些经验,并能为我指明正确的方向。类似于“此父元素中允许哪些子元素,以及以什么顺序?” 让我举个例子 我有这样一个XML文档: <
让我举个例子 我有这样一个XML文档:
<A>
<Y/>
<X/>
</A>
我有一个XML模式,它说
的内容必须是
后跟
。现在很明显,如果我尝试根据模式验证文档,它会失败,因为
和
的顺序错误。但是我事先知道我的文档是“错误的”,所以我现在还没有使用模式进行验证。但是,我知道我的文档中包含了模式定义的所有正确元素,只是顺序不对
我想做的是以编程方式检查模式(可能使用XSOM,这是XML模式的对象模型),并询问它
的内容应该是什么。API将公开以下信息:“您需要一个
,后跟一个
”
因此,我获取我的XML文档(使用DOM API)并进行相应的重新排列,以便现在文档将根据模式进行验证
理解这里的XSOM很重要——它是一个java API,表示XML模式中包含的信息,而不是实例文档中包含的信息
我不想做的是从模式生成代码,因为模式在构建时是未知的。此外,XSLT没有任何用处,因为元素的正确顺序完全由模式中包含的数据字典决定
希望这已经足够明确了。您的问题可以转化为:您有一个与模式不匹配的XSM文件,您希望将其转换为有效的文件 使用XSOM,您可以读取XSD中的结构,也许还可以分析XML,但它仍然需要从无效表单到有效表单的额外映射。样式表的使用会容易得多,因为您将遍历XML,使用XPath节点以正确的顺序处理元素。对于希望苹果优先于梨的XML,样式表将首先复制苹果节点(/Fruit/apple),然后再复制梨节点。这样,无论旧文件中的顺序如何,它们在新文件中的顺序都是正确的 使用XSOM可以读取XSD并生成样式表来重新排序数据。然后使用该样式表转换XML。一旦XSOM为XSD生成了一个样式表,您就可以重复使用该样式表,直到修改XSD或需要另一个XSD为止 当然,您可以使用XSOM以正确的顺序立即复制节点。但由于这意味着您的代码必须遍历所有节点和子节点,因此可能需要一些时间才能完成处理。样式表也可以做到这一点,但transformer将能够更快地处理它。它可以直接处理数据,而Java代码必须通过XMLDocument属性获取/设置每个节点。
因此,我将使用XSOM为XSD生成一个样式表,该样式表只需逐个节点复制XML节点即可反复使用。样式表只需要在XSD发生更改时重写,而且它的执行速度比Java API需要遍历节点本身时快。样式表不关心顺序,因此它总是以正确的顺序结束。
为了让它更有趣,您可以跳过XSOM,尝试使用读取XSD的样式表来生成另一个样式表。生成的样式表将按照样式表中定义的确切顺序复制XML节点。会很复杂吗?实际上,样式表需要为每个元素生成模板,并确保该元素中的子元素按正确的顺序处理 当我想到这一点时,我想知道以前是否已经这样做过。它将非常通用,能够处理几乎所有的XSD/XML
让我们看看。。。使用“//xsd:element/@name”可以获得模式中的所有元素名称。每个唯一的名称都需要翻译成模板。在这些模板中,您需要处理特定元素的子节点,这稍微复杂一些。元素可以有一个引用,您需要遵循该引用。否则,请获取所有子xsd:element节点。对此我还没有一个好的答案,但我必须注意,这里可能存在歧义。考虑这个模式:
<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>
这可以通过重新排序
和
,或者通过重新排序
和
来符合模式。似乎没有任何理由选择一个而不是另一个。基本上,您希望获取根元素,然后从那里递归地查看文档中的子元素和架构中定义的子元素,并使顺序匹配
我将为您提供一个C#语法解决方案,因为这是我日夜编写的代码,它非常接近Java。注意,我会
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 ] );
}
}
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();
}
}