From f875ab2fc781ad77a12f35a74d8345e836bb5dfe Mon Sep 17 00:00:00 2001 From: Denys Khalitov <187554832+DarkLight606@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:19:47 +0300 Subject: [PATCH] Escape ~ and / in JSONPointer.toURIFragment() toURIFragment() URL-encoded each reference token but skipped the ~0/~1 escaping from RFC 6901. Tokens are stored as literal key names, so a key containing / or ~ produced a fragment that parsed back to a different pointer. Apply escape() before encoding, as toString() already does. Fixes #1082 --- src/main/java/org/json/JSONPointer.java | 2 +- .../java/org/json/junit/JSONPointerTest.java | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/json/JSONPointer.java b/src/main/java/org/json/JSONPointer.java index f4cff541c..83a0690d7 100644 --- a/src/main/java/org/json/JSONPointer.java +++ b/src/main/java/org/json/JSONPointer.java @@ -276,7 +276,7 @@ public String toURIFragment() { try { StringBuilder rval = new StringBuilder("#"); for (String token : this.refTokens) { - rval.append('/').append(URLEncoder.encode(token, ENCODING)); + rval.append('/').append(URLEncoder.encode(escape(token), ENCODING)); } return rval.toString(); } catch (UnsupportedEncodingException e) { diff --git a/src/test/java/org/json/junit/JSONPointerTest.java b/src/test/java/org/json/junit/JSONPointerTest.java index bbea5d2a3..7095d45da 100644 --- a/src/test/java/org/json/junit/JSONPointerTest.java +++ b/src/test/java/org/json/junit/JSONPointerTest.java @@ -219,9 +219,29 @@ public void toURIFragment() { assertEquals("#/c%25d", new JSONPointer("/c%d").toURIFragment()); assertEquals("#/e%5Ef", new JSONPointer("/e^f").toURIFragment()); assertEquals("#/g%7Ch", new JSONPointer("/g|h").toURIFragment()); - assertEquals("#/m%7En", new JSONPointer("/m~n").toURIFragment()); + assertEquals("#/m%7E0n", new JSONPointer("/m~n").toURIFragment()); } - + + @Test + public void toURIFragmentEscaping() { + JSONPointer pointer = JSONPointer.builder() + .append("obj") + .append("other~key").append("another/key") + .append(0) + .build(); + assertEquals("#/obj/other%7E0key/another%7E1key/0", pointer.toURIFragment()); + assertEquals("val", new JSONPointer(pointer.toURIFragment()).queryFrom(document)); + } + + @Test + public void toURIFragmentRoundTrip() { + JSONObject object = new JSONObject().put("~1", "tilde one").put("/", "slash"); + JSONPointer tildeOne = JSONPointer.builder().append("~1").build(); + JSONPointer slash = JSONPointer.builder().append("/").build(); + assertEquals("tilde one", object.query(tildeOne.toURIFragment())); + assertEquals("slash", object.query(slash.toURIFragment())); + } + @Test public void tokenListIsCopiedInConstructor() { JSONPointer.Builder b = JSONPointer.builder().append("key1");