質問

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:

  1. 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.
  2. 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

役に立ちましたか?

解決

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:

codeNode.children().find { it.name() == "resultReporter" }

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:

    def reporterNode = codeNode.children().find { it.name() == "resultReporter" }
if( !reporterNode.isEmpty() ) { //reporter node exists - update it
    reporterNode.replaceNode { node ->
        'lab:resultReporter'() {
            'lab:enteredEmployeeId'(nextResult.getReporter().getEnteredEmployeeId())
        }
    }
} else {    //element does not exist - add new one
    codeNode.appendNode {
        'lab:resultReporter' {
            'lab:enteredEmployeeId'(nextResult.getReporter().getEnteredEmployeeId())
        }
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top