如何用Perl的XML::LibXML替换XML中特定父节点的子节点值?
Hey there! Let's fix that issue you're having with XML::LibXML. The core problem here is that your current XPath queries are globally searching for <number> and <busNumber> across the entire document, so they always pick up the first matching nodes (from the Consignee business entry) instead of targeting the ones under the Shipper's <business> node.
问题根源
When you run $doc->findnodes('//shipment//business//number//text()'), the // syntax starts searching from the root of the document every time—ignoring the fact that you already found the Shipper's <business> node. That's why the wrong nodes get modified.
修正后的代码
Here's the adjusted script that targets only the Shipper's <number> and <busNumber> values:
use strict; use warnings; use XML::LibXML; my $parser = XML::LibXML->new; my $doc = $parser->parse_file("Test.xml"); my $root = $doc->getDocumentElement(); ## ERROR KEY ENTRY_MISSING if ( my $valdExpHBill = $doc->findnodes('//shipment//flag[string()="true"]') ) { print "valid flag is true\n"; ## ID_MISSING # Directly locate all <business> nodes where Type is "Shipper" my @shipperBusinessNodes = $doc->findnodes('//shipment//business[Type[string()="Shipper"]]'); foreach my $businessNode (@shipperBusinessNodes) { print "Shipper business exist under this shipment\n"; # Use relative XPath to find <number> within the current <business> node if (my ($numberText) = $businessNode->findnodes('./number/text()')) { $numberText->setData(' '); } # Do the same for <busNumber> if (my ($busNumberText) = $businessNode->findnodes('./busNumber/text()')) { $busNumberText->setData(' '); } } # Save the modified XML my $EDE5 = $doc->toFile("ID_MISSING.xml", 1); }
关键改动说明
Target the right
nodes upfront :
The XPath//shipment//business[Type[string()="Shipper"]]directly finds all<business>nodes that have a<Type>child with the value "Shipper"—no need to find the Type node first and then backtrack.Use relative XPath for child nodes:
Instead of using//to search globally, we use./number/text()which searches only within the current<business>node (the one we already identified as Shipper). This ensures we modify exactly the nodes we want.Handle multiple Shipper nodes (just in case):
By looping over@shipperBusinessNodes, the script will work even if there are multiple Shipper entries in your XML—each one's<number>and<busNumber>will be cleared.
验证结果
With this code, your output XML will match your expected result:
<shipment> <flag>true</flag> <business> <Type>Consignee</Type> <number>G0213</number> <busNumber>3006</busNumber> </business> <business> <Type>Shipper</Type> <number> </number> <busNumber> </busNumber> </business> </shipment>
内容的提问来源于stack exchange,提问作者Amareesh

