diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java index 6e68b7b73..9fea7ad6d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java @@ -74,7 +74,7 @@ public class ExpressionNode { Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue, HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo, // R3 functions - Encode, Decode, Escape, Trim, Split, Join, + Encode, Decode, Escape, Unescape, Trim, Split, Join, // Local extensions to FHIRPath HtmlChecks, AliasAs, Alias; @@ -139,6 +139,7 @@ public class ExpressionNode { if (name.equals("encode")) return Function.Encode; if (name.equals("decode")) return Function.Decode; if (name.equals("escape")) return Function.Escape; + if (name.equals("unescape")) return Function.Unescape; if (name.equals("trim")) return Function.Trim; if (name.equals("split")) return Function.Split; if (name.equals("join")) return Function.Join; @@ -221,6 +222,7 @@ public class ExpressionNode { case Encode : return "encode"; case Decode : return "decode"; case Escape : return "escape"; + case Unescape : return "unescape"; case Trim : return "trim"; case Split : return "split"; case Join : return "join"; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index f24272253..79132ec52 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -1151,6 +1151,7 @@ public class FHIRPathEngine { case Encode: return checkParamCount(lexer, location, exp, 1); case Decode: return checkParamCount(lexer, location, exp, 1); case Escape: return checkParamCount(lexer, location, exp, 1); + case Unescape: return checkParamCount(lexer, location, exp, 1); case Trim: return checkParamCount(lexer, location, exp, 0); case Split: return checkParamCount(lexer, location, exp, 1); case Join: return checkParamCount(lexer, location, exp, 1); @@ -2661,6 +2662,9 @@ public class FHIRPathEngine { case Escape: checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); + case Unescape: + checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Trim: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Split: @@ -2846,6 +2850,7 @@ public class FHIRPathEngine { case Encode : return funcEncode(context, focus, exp); case Decode : return funcDecode(context, focus, exp); case Escape : return funcEscape(context, focus, exp); + case Unescape : return funcUnescape(context, focus, exp); case Trim : return funcTrim(context, focus, exp); case Split : return funcSplit(context, focus, exp); case Join : return funcJoin(context, focus, exp); @@ -2958,6 +2963,23 @@ public class FHIRPathEngine { return result; } + private List funcUnescape(ExecutionContext context, List focus, ExpressionNode exp) { + List nl = execute(context, focus, exp.getParameters().get(0), true); + String param = nl.get(0).primitiveValue(); + + List result = new ArrayList(); + if (focus.size() == 1) { + String cnt = focus.get(0).primitiveValue(); + if ("html".equals(param)) { + result.add(new StringType(Utilities.unescapeXml(cnt))); + } else if ("json".equals(param)) { + result.add(new StringType(Utilities.unescapeJson(cnt))); + } + } + + return result; + } + private List funcTrim(ExecutionContext context, List focus, ExpressionNode exp) { List result = new ArrayList(); if (focus.size() == 1) { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java index 20e79d29a..26421fafe 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java @@ -213,6 +213,9 @@ public class FHIRPathTests { Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString())); } else { Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType())); + if (!(v.equals(((PrimitiveType) outcome.get(i)).asStringValue()))) { + System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression)); + } Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression)); } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 4343708b1..48dac02a5 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -509,6 +509,55 @@ public class Utilities { return b.toString(); } + public static String unescapeJson(String json) throws FHIRException { + if (json == null) + return null; + + StringBuilder b = new StringBuilder(); + int i = 0; + while (i < json.length()) { + if (json.charAt(i) == '\\') { + i++; + char ch = json.charAt(i); + switch (ch) { + case '"': + b.append('b'); + break; + case '\\': + b.append('\\'); + break; + case '/': + b.append('/'); + break; + case 'b': + b.append('\b'); + break; + case 'f': + b.append('\f'); + break; + case 'n': + b.append('\n'); + break; + case 'r': + b.append('\r'); + break; + case 't': + b.append('\t'); + break; + case 'u': + String hex = json.substring(i+1, i+5); + b.append((char) Integer.parseInt(hex, 16)); + break; + default: + throw new FHIRException("Unknown JSON escape \\"+ch); + } + } else + b.append(json.charAt(i)); + i++; + } + return b.toString(); + } + public static boolean isPlural(String word) { word = word.toLowerCase();