2

A status in XML needs to change before it gets forwarded. If RESPONSE.OUTBOUND.STATUS is equal to "ERR", it needs to say "FAILURE" instead. Other messages that STATUS may contain must remain as is.

Sample XML before processing:

<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE>
   <ID>9497585</ID>
   <DATE>2020-10-01</DATE>
   <TIME>18:38:04</TIME>
   <OUTBOUND>
      <CODE>921</CODE>
      <STATUS>ERR</STATUS>
      <DESC>Manufacturing flaw</DESC>
   </OUTBOUND>
   <ORIGIN>
      <METHOD>POST</METHOD>
      <STATUS>200 OK</STATUS>
      <CLIENTID>29834</CLIENTID>
      <DIAG>330</DIAG>
      <NOTES>XRAY revealed air pockets.</NOTES>
   </ORIGIN>
</RESPONSE>

DataWeave:

%dw 2.0
output application/xml
---
payload 
   - "RESPONSE" ++
   { "RESPONSE" : 
        (payload.RESPONSE - "OUTBOUND") ++
        { "OUTBOUND" : 
            (payload.RESPONSE.OUTBOUND - "STATUS") ++ 
            {"STATUS" : 
               if (payload.RESPONSE.OUTBOUND.STATUS == "ERR") 
                  "FAILURE"  
               else 
                  payload.RESPONSE.OUTBOUND.STATUS
            } 
        }
   } 

This is the output:

<?xml version='1.0' encoding='UTF-8'?>
<RESPONSE>
  <ID>9497585</ID>
  <DATE>2020-10-01</DATE>
  <TIME>18:38:04</TIME>
  <ORIGIN>
    <METHOD>POST</METHOD>
    <STATUS>OK</STATUS>
    <CLIENTID>29834</CLIENTID>
    <DIAG>330</DIAG>
    <NOTES>XRAY revealed air pockets.</NOTES>
  </ORIGIN>
  <OUTBOUND>
    <CODE>921</CODE>
    <DESC>Manufacturing flaw</DESC>
    <STATUS>FAILURE</STATUS>
  </OUTBOUND>
</RESPONSE>

This works in a way, but to change a single value, it seems unnecessarily complex. It's like using a sledgehammer because I couldn't find a scalpel. Is there a simpler way to get to the node and change it?

Also, XML is node-order dependent. By removing and then reinserting the sub-node, I end up changing the sequence of the nodes ("OUTBOUND" now comes after "ORIGIN"). This will probably create grief down the road.

Tony
  • 2,658
  • 2
  • 31
  • 46

6 Answers6

2

The simplest way to do this is to use the update operator

%dw 2.0
output application/xml
---
payload update {
    case status at .RESPONSE.OUTBOUND.STATUS if(status == "ERR") ->  "FAILURE"
}

Simple and clear ;)

machaval
  • 4,969
  • 14
  • 20
  • Hi @machaval, a question: why does `update` sometimes require `import * from dw::util::Values` but not here? – Tony Oct 29 '20 at 06:03
  • The update in values is just a function that was introduced in 4.2 and receives an array of strings with the oath to update. In 4.3 we introduced the update operator. Now update is part of the language so we could add better support and tooling. That is why – machaval Oct 29 '20 at 07:35
2

Using the update function , Mariano gave me a different approach to solve this as well.

The function expects a path to the element described in an array format.

Its raining answers for this question :) .

%dw 2.0
import update from dw::util::Values
output application/xml
---
payload  update ["RESPONSE", "OUTBOUND", "STATUS"] with (value) -> if(value == "ERR") "Failure" else value 
Salim Khan
  • 4,233
  • 11
  • 15
0

This should help.

%dw 2.0
output application/xml
fun replaceElementValue(value:Any, nametoReplace: String, newValue: Any) = do {
    value match {
        case obj is Object -> obj mapObject ((value, key, index) -> 
            if(key ~= nametoReplace and value == "ERR")
                (key): newValue
            else    
                (key) : replaceElementValue(value, nametoReplace, newValue)
        )
        else -> value
    }
}   
---
replaceElementValue(payload,"STATUS","FAILRUE")

Slightly modified the script mentioned here to help achieve what you are looking for.

enter image description here

Salim Khan
  • 4,233
  • 11
  • 15
  • 1
    Hi Salim, thanks for taking a shot, but this also examines and potentially replaces the STATUS node in the ORIGIN branch. I only want to change the STATUS node in OUTBOUND. – Tony Oct 25 '20 at 06:48
0

Extension of your attempt. Preserves ordering of the nodes Try with this script.

%dw 2.0
output application/xml
var outbound = payload.RESPONSE - "ORIGIN"
var origin  = payload.RESPONSE - "OUTBOUND" - "ID" - "DATE" - "TIME"
---
{
 a: outbound - "OUTBOUND" ++ { "OUTBOUND" : 
            (payload.RESPONSE.OUTBOUND - "STATUS") ++ 
            {"STATUS" : 
               if (payload.RESPONSE.OUTBOUND.STATUS == "ERR") 
                  "FAILURE"  
               else 
                  payload.RESPONSE.OUTBOUND.STATUS
            } 
        } ++
        origin
}
Salim Khan
  • 4,233
  • 11
  • 15
  • 1
    Hi Salim, this retains major node order but not the placement of STATUS. It ends up shifting to the last node in OUTBOUND. I can't help but think this should be easier. – Tony Oct 28 '20 at 20:55
0

If you don't expect any other tag to have the ERR value, I would use one of the following two functions:

%dw 2.0
output application/xml

var data = read(
'<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE>
   <ID>9497585</ID>
   <DATE>2020-10-01</DATE>
   <TIME>18:38:04</TIME>
   <OUTBOUND>
      <CODE>921</CODE>
      <STATUS>ERR</STATUS>
      <DESC>Manufacturing flaw</DESC>
   </OUTBOUND>
   <ORIGIN>
      <METHOD>POST</METHOD>
      <STATUS>200 OK</STATUS>
      <CLIENTID>29834</CLIENTID>
      <DIAG>330</DIAG>
      <NOTES>XRAY revealed air pockets.</NOTES>
   </ORIGIN>
</RESPONSE>',
"application/xml"
)

// Traverse the XML and replace the String ERR with FAILURE
fun traverse(o: Object) = o mapObject (
    if ($$ ~= "OUTBOUND") {($$): traverse($)} else {($$):$}
)
fun traverse(s: String) = (
    if (s == "ERR") "FAILURE" else s
)

// Decouple the replacement from the traversal.  This is a more
// flexible solution
fun traverseFn(o: Object,fn, tag: String = "OUTBOUND") = o mapObject (
    if ($$ ~= tag) {($$): ($ traverseFn fn)} else {($$): $}
)
fun traverseFn(s: String, fn) = fn(s)

---
//traverse(data)

data traverseFn (
    (s) -> s match {
        case "ERR" -> "FAILURE"
        else -> $
    }
)
George
  • 2,758
  • 12
  • 16
  • 1
    This would have ORIGIN STATUS output as FAILURE in case the ORIGIN STATUS input has ERR as the value alongside the OUTBOUND STATUS. He wants only to change the value of OUTBOUND.STATUS. Thus breaking it down and adding the object back after the update seems to be the only ideal way, since there is no way to uniquely differentiate the two STATUS fields. Unless the parent of the current STATUS key can be checked and compared to "ORIGIN". – Salim Khan Oct 26 '20 at 06:52
  • Oh I see, thank you @SalimKhan! @Tony I updated the answer, there was just a quick change to when you recurse. – George Oct 26 '20 at 10:18
  • @SalimKhan How so? :) – George Oct 29 '20 at 11:28
0

Try with this -

Using update function - documented here

%dw 2.0
import * from dw::util::Values
output application/xml
var resp = payload.RESPONSE - "OUTBOUND" - "ORIGIN"
var outbound = if(payload.RESPONSE.OUTBOUND.STATUS == "ERR") (payload.RESPONSE.OUTBOUND update "STATUS" with "FAILURE") else (payload.RESPONSE.OUTBOUND)
var origin  = payload.RESPONSE - "OUTBOUND" - "ID" - "DATE" - "TIME"
---
{
    RESPONSE: resp ++ {OUTBOUND: outbound}  ++ origin
}
Salim Khan
  • 4,233
  • 11
  • 15