Skip to content

Commit

Permalink
Fix canonical representations of JSONPath and JSONPathNode
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Jan 24, 2025
1 parent 864f292 commit 304648d
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "test/cts"]
path = test/cts
url = [email protected]:jsonpath-standard/jsonpath-compliance-test-suite.git
[submodule "test/nts"]
path = test/nts
url = [email protected]:jg-rp/jsonpath-compliance-normalized-paths.git
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.3.2] - unreleased

- Fix normalized string representations of node locations as returned by `JSONPathNode.path`.
- Fix canonical string representations of instances of `JSONPath`, as returned by `to_s`.

## [0.3.1] - 2024-12-05

- Fix JSON Patch `move` and `copy` operations when using the special JSON Pointer token `-`.
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
json_p3 (0.3.1)
json_p3 (0.3.2)

GEM
remote: https://rubygems.org/
Expand Down
4 changes: 2 additions & 2 deletions lib/json_p3/filter.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

require "json"
require_relative "function"
require_relative "serialize"

module JSONP3 # rubocop:disable Style/Documentation
# Base class for all filter expression nodes.
Expand Down Expand Up @@ -85,7 +85,7 @@ class BooleanLiteral < FilterExpressionLiteral; end
# A double or single quoted string literal.
class StringLiteral < FilterExpressionLiteral
def to_s
JSON.generate(@value)
JSONP3.canonical_string(@value)
end
end

Expand Down
4 changes: 3 additions & 1 deletion lib/json_p3/node.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require_relative "serialize"

module JSONP3
# A JSON-like value and its location.
class JSONPathNode
Expand All @@ -19,7 +21,7 @@ def initialize(value, location, root)
# Return the normalized path to this node.
# @return [String] the normalized path.
def path
segments = @location.flatten.map { |i| i.is_a?(String) ? "['#{i}']" : "[#{i}]" }
segments = @location.flatten.map { |i| i.is_a?(String) ? "[#{JSONP3.canonical_string(i)}]" : "[#{i}]" }
"$#{segments.join}"
end

Expand Down
2 changes: 1 addition & 1 deletion lib/json_p3/selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def singular?
end

def to_s
@name.inspect
JSONP3.canonical_string(@name)
end

def ==(other)
Expand Down
13 changes: 13 additions & 0 deletions lib/json_p3/serialize.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

require "json"

module JSONP3 # rubocop:disable Style/Documentation
TRANS = { "\\\"" => "\"", "'" => "\\'" }.freeze

# Return _value_ formatted as a canonical string literal.
# @param value [String]
def self.canonical_string(value)
"'#{(JSON.dump(value)[1..-2] || raise).gsub(/('|\\")/, TRANS)}'"
end
end
2 changes: 1 addition & 1 deletion lib/json_p3/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module JSONP3
VERSION = "0.3.1"
VERSION = "0.3.2"
end
4 changes: 3 additions & 1 deletion sig/json_p3.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ module JSONP3
# @param value [String]
# @param quote [String] one of '"' or "'".
# @param token [Token]
# @return [String] A new string without escape seqeuences.
# @return [String] A new string without escape sequences.
def self.unescape_string: (String value, String quote, Token token) -> String

def self.decode_hex_char: (untyped value, untyped index, Token token) -> ::Array[untyped]
Expand All @@ -938,6 +938,8 @@ module JSONP3
def self.low_surrogate?: (untyped code_point) -> untyped

def self.code_point_to_string: (untyped code_point, Token token) -> untyped

def self.canonical_string: (untyped String) -> ::String
end

module JSONP3
Expand Down
1 change: 1 addition & 0 deletions test/nts
Submodule nts added at e3539f
36 changes: 36 additions & 0 deletions test/test_nts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require "json"
require "test_helper"

class TestNormalizedPathTestSuite < Minitest::Spec
make_my_diffs_pretty!

nts = JSON.parse(File.read("test/nts/normalized_paths.json"))

describe "normalized path test suite" do
nts["tests"].each do |test_case|
it test_case["name"] do
nodes = JSONP3.find(test_case["query"], test_case["document"])
paths = nodes.map(&:path)
_(paths).must_equal(test_case["paths"])
end
end
end
end

class TestCanonicalPathTestSuite < Minitest::Spec
make_my_diffs_pretty!

nts = JSON.parse(File.read("test/nts/canonical_paths.json"))

describe "canonical path test suite" do
nts["tests"].each do |test_case|
it test_case["name"] do
query = JSONP3.compile(test_case["query"])
path = query.to_s
_(path).must_equal(test_case["canonical"])
end
end
end
end

0 comments on commit 304648d

Please sign in to comment.