Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jsonpatch.Equal : strange behavior on handling literal unicode string equality #172

Closed
luc-alquier opened this issue Mar 15, 2023 · 1 comment · Fixed by #195
Closed

Comments

@luc-alquier
Copy link

Description

Json string "λJohn" is not equal to "\u03BBJohn" for the function jsonpatch.Equal

I hope to not misinterpret rfc which is stating:

strings: are considered equal if they contain the same number of
Unicode characters and their code points are byte-by-byte equal.

Expected behavior

Equality

Step to reproduce

package main

import (
	"fmt"
	jsonpatch "github.com/evanphx/json-patch"
)

//
func main() {
	original := []byte(`{"name": "λJohn", "age": 24, "height": 3.21}`)
	similar := []byte(`
			{
				"age": 24,
				"height": 3.21,
				"name": "\u03BBJohn"
			}
		`)
	different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`)

	if jsonpatch.Equal(original, similar) {
		fmt.Println(`"original" is structurally equal to "similar"`)
	}

	if !jsonpatch.Equal(original, different) {
		fmt.Println(`"original" is _not_ structurally equal to "different"`)
	}

}

output:

"original" is _not_ structurally equal to "different"

Test with other implementation

using System;
using System.Text.Json;

using System.Text.Json.JsonDiffPatch;					
public class Program
{
	public static void Main()
	{
		var doc1 = JsonDocument.Parse("""{"name": "λJohn", "age": 24, "height": 3.21}""");
		var doc2 = JsonDocument.Parse("""{  "age": 24,   "height": 3.21, "name": "\u03BBJohn"   }""");
		var equal = doc1.DeepEquals(doc2);
		var textEqual = doc1.DeepEquals(doc2, JsonElementComparison.RawText);
		var semanticEqual = doc1.DeepEquals(doc2, JsonElementComparison.Semantic);	
	    Console.WriteLine($"equal= { equal }");
	    Console.WriteLine($"textEqual= { textEqual }");
	    Console.WriteLine($"semanticEqual= { semanticEqual }");
		
	}
	
}

Output:

equal= True
textEqual= True
semanticEqual= True
@shadjiiski
Copy link

This also causes the "test" operation of JSON patches to fail whenever the document and the path body have different representations of the same content:

package main

import (
	"encoding/json"
	"fmt"

	jsonpatch "github.com/evanphx/json-patch"
)

func testJsonPatch(patchOps []byte) {
	currentState := map[string]any{
		"spec": map[string]any{
			"test": "<",
		},
	}

	// will escape "<" to \u003c
	currentStateJsonBytes, err := json.Marshal(currentState)
	if err != nil {
		panic(err)
	}

	patch, err := jsonpatch.DecodePatch(patchOps)
	if err != nil {
		panic(err)
	}

	patchedBytes, err := patch.Apply(currentStateJsonBytes)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(patchedBytes))
}

func main() {
	// Patch guessing the internal serialization specifics
	testJsonPatch([]byte(`[{"op":"test","path":"/spec/test","value":"\u003c"}]`)) // {"spec":{"test":"\u003c","unicode":"ß"}}

	// Patch with no internal knowledge
	testJsonPatch([]byte(`[{"op":"test","path":"/spec/test","value":"<"}]`))      // panic: testing value /spec/test failed: test failed
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants