I am trying to replace elements within an XML string, in the fastest most efficient way possible. Consider the code:
final String rawXml = "
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<cnpOnlineRequest merchantId=\"017872345\" merchantSdk=\"Java;12.0.0\" version=\"12.0\" xmlns=\"http://www.vantivcnp.com/schema\">
<authentication>
<user>AUSER</user>
<password>pa5Sw0rd!</password>
</authentication>
<authorization reportGroup=\"Default Report Group\" id=\"87654321\">
<orderId>Merchant Order Id</orderId>
<amount>1299</amount>
<orderSource>ecommerce</orderSource>
<billToAddress>
<addressLine1>5 Some Road</addressLine1>
<city>Townsville</city>
<state>Alabama</state>
<zip>31431</zip>
<country>US</country>
</billToAddress>
<card>
<type>VI</type>
<number>1234123412341234</number>
<expDate>0718</expDate>
<cardValidationNum>999</cardValidationNum>
<pin>1234</pin>
</card>
</authorization>
</cnpOnlineRequest>";
final String expectedXml = "
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<cnpOnlineRequest merchantId=\"017872345\" merchantSdk=\"Java;12.0.0\" version=\"12.0\" xmlns=\"http://www.vantivcnp.com/schema\">
<authentication>
<user>AUSER</user>
<password>---sanitised---</password>
</authentication>
<authorization reportGroup=\"Default Report Group\" id=\"87654321\">
<orderId>Merchant Order Id</orderId>
<amount>1299</amount>
<orderSource>ecommerce</orderSource>
<billToAddress>
<addressLine1>5 Some Road</addressLine1>
<city>Townsville</city>
<state>Alabama</state>
<zip>31431</zip>
<country>US</country>
</billToAddress>
<card>
<type>VI</type>
<number>---sanitised---</number>
<expDate>0718</expDate>
<cardValidationNum>---sanitised---</cardValidationNum>
<pin>---sanitised---</pin>
</card>
</authorization>
</cnpOnlineRequest>";
final String[] elements = { "password", "number", "cardValidationNum", "pin" };
final Map<String,String> replacements = new LinkedHashMap<>();
for (final String element : elements) {
final String regexp = String.format("<%s>.*</%s>", element, element);
final String replacement = String.format("<%s>---sanitised---</%s>", element, element);
replacements.put(regexp, replacement);
}
final String regexp = "%(" + StringUtils.join(replacements.ketSet(), "|") + ")%";
final Pattern pattern = Pattern.compile(regexp, Pattern.DOTALL);
final Matcher matcher = pattern.matcher(rawXml);
final StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(buffer, replacements.get(matcher.group(1)));
}
final String sanitisedXml = buffer.toString();
assertThat(sanitisedXml, equalTo(expectedXml));
What is happening is that find() does not find anything, so the buffer is empty, and the assert fails. I have also tried replacing "%(" and ")%" with ".*(" and ").*", then find() will work, but there is only 1 group, and it contains the entire string.
Clarification: This must be fast, and there are more elements than those listed. I want to parse the string only once, so replaceAll for each regexp and replacement, is not an option. Neither is unmarshalling the XML into an object, using code to replace all the values, then marshalling the object back into XML.