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.

6 Answers 6

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 ;)

Sign up to request clarification or add additional context in comments.

2 Comments

Hi @machaval, a question: why does update sometimes require import * from dw::util::Values but not here?
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
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 

Comments

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

1 Comment

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.
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
}

1 Comment

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.
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
}

1 Comment

Try with this approach Tony and see if it makes a diff?
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 -> $
    }
)

3 Comments

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".
Oh I see, thank you @SalimKhan! @Tony I updated the answer, there was just a quick change to when you recurse.
@SalimKhan How so? :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.