Groovy XMLSlurper 更新文档以匹配架构
我遇到了一个似乎无法解决的问题,希望你们中的一些知识渊博的专家能够提供解决方案。
我有一个包含序列的 XSD 文档。如您所知,这意味着所有元素必须按指定的顺序出现。我需要使用它,因为其中一些也是可选的(minOccurs =“0”)。
以下是架构的简化部分:
<xs:element name="result">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:resultCode"/>
<xs:element ref="tns:resultAbbreviations" minOccurs="0"/>
<xs:element ref="tns:resultReporter" minOccurs="0"/>
<xs:element ref="tns:vendorData" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
XML 文档的相关部分:
<lab:order>
<lab:results>
<lab:result>
<lab:resultCode>005009</lab:resultCode>
<lab:resultAbbreviations>
<lab:resultAbbreviation>FOO</lab:resultAbbreviation>
</lab:resultAbbreviations>
<lab:resultReporter>
<lab:enteredEmployeeId>86118</lab:enteredEmployeeId>
</lab:resultReporter>
<lab:vendorData value="123" key="ABC"/>
<lab:vendorData value="ABC" key="123"/>
</lab:result>
<lab:result>
<lab:resultCode>005025</lab:resultCode>
</lab:result>
...
我需要能够做两件事:
- 如果元素存在,则更新其值。例如,将 resultCode“005009”的 EnteredEmployeeId 值更改为“EntVal”。这需要查找该元素是否存在。
- 如果元素不存在,请将其添加到根据架构将通过验证的位置。例如,为 resultCode“005025”添加 resultReporter 和 EnteredEmployeeId。请注意,有些可选元素可能存在,也可能不存在,如上面的 XML 片段所示。
我已经能够将一个节点添加到“结果”节点的末尾,但无法使查找工作进行更新,也无法将节点插入到正确的位置以满足 XSD。下面是代码:
...
//-- ResultReporter: enteredEmployeeId, verifiedEmployeeId
// Must add to proper result, based on code
ResultReporter reporter = nextResult.getReporter();
NodeChild codeNode = getResultNodeFor( nextResult.getCode() );
if( codeNode != null ) { //found proper result - does reporter exist already?
def reporterNode = codeNode.find { it.name() == 'resultReporter' }
if( !reporterNode.isEmpty() ) { //reporter node exists - update it
reporterNode.'lab:enteredEmployeeId'( nextResult.getReporter().getEnteredEmployeeId() )
} else { //element does not exist - add new one
codeNode.appendNode {
'lab:resultReporter' {
'lab:enteredEmployeeId'(nextResult.getReporter().getEnteredEmployeeId())
}
}
}
} else { //not found
throw new IllegalArgumentException("Cannot add reporter for nonexistent result code: " + nextResult.getCode() );
}
...
/**
* @param aCode
* @return the Node with resultCode = aCode, else null
*/
private NodeChild getResultNodeFor( String aCode ) {
for( def nextResult : labDoc.order.results.children() ) {
if( nextResult.resultCode.text().equals(aCode) ) { //found
return nextResult;
}
}
return null; //not found
}
我正在寻找这样的 XML 输出(请注意,第一个结果的值已更新,第二个结果的值已插入 - 但在正确的位置...那里也可能有其他元素!):
<lab:order>
<lab:results>
<lab:result>
<lab:resultCode>005009</lab:resultCode>
<lab:resultAbbreviations>
<lab:resultAbbreviation>FOO</lab:resultAbbreviation>
</lab:resultAbbreviations>
<lab:resultReporter>
<lab:enteredEmployeeId>EntVal</lab:enteredEmployeeId>
</lab:resultReporter>
<lab:vendorData value="123" key="ABC"/>
<lab:vendorData value="ABC" key="123"/>
</lab:result>
<lab:result>
<lab:resultCode>005025</lab:resultCode>
<lab:resultReporter>
<lab:enteredEmployeeId>EntVal</lab:enteredEmployeeId>
</lab:resultReporter>
</lab:result>
...
所以:谁能告诉我如何进行这项工作?谢谢!标记
I'm having an issue I can't seem to work my way past and hoping some of you knowledgeable mavens can provide a solution.
I have a document with an XSD containing a sequence. As you know, this means that all the elements must appear in the specified order. I need to use this because some of them are optional too (minOccurs = "0").
Here is a simplified portion of the schema:
<xs:element name="result">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:resultCode"/>
<xs:element ref="tns:resultAbbreviations" minOccurs="0"/>
<xs:element ref="tns:resultReporter" minOccurs="0"/>
<xs:element ref="tns:vendorData" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
A related portion of the XML document:
<lab:order>
<lab:results>
<lab:result>
<lab:resultCode>005009</lab:resultCode>
<lab:resultAbbreviations>
<lab:resultAbbreviation>FOO</lab:resultAbbreviation>
</lab:resultAbbreviations>
<lab:resultReporter>
<lab:enteredEmployeeId>86118</lab:enteredEmployeeId>
</lab:resultReporter>
<lab:vendorData value="123" key="ABC"/>
<lab:vendorData value="ABC" key="123"/>
</lab:result>
<lab:result>
<lab:resultCode>005025</lab:resultCode>
</lab:result>
...
I need to be able to do two things:
- If an element exists, update its value. E.g. change the enteredEmployeeId value to "EntVal" for resultCode "005009". This requires finding if that element exists.
- If an element does not exist, add it at a location that will pass validation according to the schema. E.g. add resultReporter and enteredEmployeeId for resultCode "005025". Note that there are optional elements that may or may not be there as shown in the XML snippet above.
I have been able to add a Node to the end of the "result" Node, but can't get the find to work to update nor get the Node inserted at the proper spot to satisfy the XSD. Here is the code:
...
//-- ResultReporter: enteredEmployeeId, verifiedEmployeeId
// Must add to proper result, based on code
ResultReporter reporter = nextResult.getReporter();
NodeChild codeNode = getResultNodeFor( nextResult.getCode() );
if( codeNode != null ) { //found proper result - does reporter exist already?
def reporterNode = codeNode.find { it.name() == 'resultReporter' }
if( !reporterNode.isEmpty() ) { //reporter node exists - update it
reporterNode.'lab:enteredEmployeeId'( nextResult.getReporter().getEnteredEmployeeId() )
} else { //element does not exist - add new one
codeNode.appendNode {
'lab:resultReporter' {
'lab:enteredEmployeeId'(nextResult.getReporter().getEnteredEmployeeId())
}
}
}
} else { //not found
throw new IllegalArgumentException("Cannot add reporter for nonexistent result code: " + nextResult.getCode() );
}
...
/**
* @param aCode
* @return the Node with resultCode = aCode, else null
*/
private NodeChild getResultNodeFor( String aCode ) {
for( def nextResult : labDoc.order.results.children() ) {
if( nextResult.resultCode.text().equals(aCode) ) { //found
return nextResult;
}
}
return null; //not found
}
I am looking for an XML output like this (note that the first result's value is updated, the second one is inserted - but at the right place... there could be other elements there too!):
<lab:order>
<lab:results>
<lab:result>
<lab:resultCode>005009</lab:resultCode>
<lab:resultAbbreviations>
<lab:resultAbbreviation>FOO</lab:resultAbbreviation>
</lab:resultAbbreviations>
<lab:resultReporter>
<lab:enteredEmployeeId>EntVal</lab:enteredEmployeeId>
</lab:resultReporter>
<lab:vendorData value="123" key="ABC"/>
<lab:vendorData value="ABC" key="123"/>
</lab:result>
<lab:result>
<lab:resultCode>005025</lab:resultCode>
<lab:resultReporter>
<lab:enteredEmployeeId>EntVal</lab:enteredEmployeeId>
</lab:resultReporter>
</lab:result>
...
So: can anyone tell me how to make this work? Thanks! Mark
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的,有进展。幸运的是,我可以控制架构,因此我将“序列”更改为“全部”。 “all”允许“minOccurs”为 0 或 1,因此我可以通过这种方式处理可选。我将可能多次出现的一个元素重构为包含可以重复的元素的一个可选元素。
由于“all”允许任何顺序(并且可选),因此我可以在任何位置添加,例如附加到末尾。
至于查找现有的,这是有效的:
我在此代码的结果上测试“isEmpty()”,看看它是否确实存在。
所以:不是通用的解决方案(例如,如果我无法更改 XSD),但可以满足我的需要。希望这对其他人有帮助。
如果有人知道如何解决这个问题,请说出来!
为了提供完整的解决方案,下面是用于更改 XML 的代码:
OK, progress. Fortunately, I have control over the schema so I changed the "sequence" to "all". "all" allows "minOccurs" of 0 or 1, so I can handle optional that way. I refactored the one element that could occur multiple times to be one optional element that contains elements that can repeat.
Since "all" allows any order (and optional), I am able to add anywhere, such as appending to the end.
As for finding the existing one, this works:
I test "isEmpty()" on the result of this code to see if it truly exists.
So: not a general solution (e.g. if I couldn't change the XSD), but works for what I need. Hope this helps others.
If someone knows how to solve this in general, please speak up!
Just to give the complete solution, here is the code that works to change the XML: