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

Undefined properties cause an exception to be thrown #32

Closed
davidpolberger opened this issue Jun 10, 2014 · 11 comments
Closed

Undefined properties cause an exception to be thrown #32

davidpolberger opened this issue Jun 10, 2014 · 11 comments
Labels

Comments

@davidpolberger
Copy link

Consider the following code:

var object = {
  test: undefined
};

var observer = jsonpatch.observe(object);
var result = jsonpatch.generate(observer);

JSON-Patch throws a SyntaxError exception when trying to generate the patch at line 421 of json-patch-duplex.js (in the function _generate):

mirror[key] = JSON.parse(JSON.stringify(obj[key]));

obj[key] is undefined, causing JSON.stringify() to return undefined, which JSON.parse() cannot handle ("Unexpected token u").

The expected behavior is that no patches should be generated, as nothing has changed.

@davidpolberger
Copy link
Author

We are using 1c5ef73 internally. This commit fixes the bug trivially, by simply not reporting on properties that are undefined. This may or may not be the appropriate long-term fix, though it does work for our needs. If it is desirable to track the addition of undefined properties in some context, a more thorough solution should be sought. I am thus not adding a pull request for this fix.

@jamesplease
Copy link
Contributor

Something like this should do the trick:

var clone = obj[key];
mirror[key] = clone ? JSON.parse(JSON.stringify(obj[key])) : clone;

We only need to create the clone if it's an object; otherwise it's passed by value and we're fine. And objects are always truthy.

//cc @Starcounter-Jack @warpech

@warpech
Copy link
Collaborator

warpech commented Jan 12, 2015

We could handle it somehow in generate step. My idea is that when a change to undefined is detected, jsonpatch.generate reports it as a remove operation. What do you think?

@jamesplease
Copy link
Contributor

@sonnyp has a convincing argument that we should not handle invalid JSON values. I've been persuaded. What do you think, @warpech?

@sonnyp
Copy link
Contributor

sonnyp commented Jan 12, 2015

@jmeas We shouldn't handle undefined in a JSON Patch document (see #32), however not sure what to do about undefined in a JavaScript object. I think best option here is to let the user configure behavior. throw error, ignore, remove, accept, ... with default of throwing an error.

@jamesplease
Copy link
Contributor

Ah, okay. Sounds good 👍

@tomalec
Copy link
Collaborator

tomalec commented Apr 6, 2015

I've just added a description at README.md, and bunch of tests.

Basically:

  1. For valid JSON document, we will generate only valid JSON Patches,
  2. For invalid JSONs (ones with undefined), we will handle it gently. .apply, .observe, .compare, .generate will use and produce JSON Patches with undefined as with any other falsy value,
  3. We will not validate entire JSON document in question,
  4. If you are not sure if your JSON is valid, and you'd like to make sure that produced JSON Patch is, use .validate.

@garthk
Copy link

garthk commented Apr 28, 2015

Is it too late to reconsider?

  • The results don't conform to RFC6902 after being run through JSON.stringify
  • The results are least astonishing to the wrong community: people who never touch JSON, despite the job of the module being to help users PATCH their JSON

Users can JSON.stringify even if they used undefined. The results simply lack the undefined values and their keys, making JSON.stringify least astonishing and robust. We can do the same.

Detail:

Like @wolfgang42 in #60, I believe

it should not be possible for fast-json-patch to emit an invalid json-patch

RFC6902 says, over and over:

The operation object MUST contain a "value" member

If value is undefined before we stringify the patch, we violate the spec. That's not strictly our fault: undefined literally doesn't exist in EMCA-404. I don't think users considering hiring us to help them switch from POST to PATCH much care for that distinction: they just need the job done. If we can't help them, they'll hire someone else.

Users can JSON.stringify despite their undefined values, making it least astonishing and robust. As @warpech observed in #60, we can do the same by treating the operation as if the before and after pictures had been run through JSON.stringify and JSON.parse.

Consider setting a value to undefined:

var myobj = { };
var observer = jsonpatch.observe( myobj );
myobj.firstName = undefined;
var patch = jsonpatch.generate( observer );

A patch of [] might be astonishing to anyone using jsonpatch purely in memory, but — again — our job is to help users PATCH their JSON. The JSON encodings of myobj before and after the mutation are identical. From the JSON point of view, the current behaviour is astonishing and, once you stringify it, non-compliant:

[ { op: 'add', path: '/firstName', value: undefined } ]
[ { "op": "add", "path": "/firstName" } ]

In short, for any mutation:

  • any patch should remain valid after encoding it with JSON.stringify
  • the patch from jsonpatch.compare should be consistent whether you run its inputs through a stringify and parse round-trip or not
  • the patch from jsonpatch.observe should match the patch from jsonpatch.compare
  • all the above should be true even if undefined is present

Edits:

  • Don't bury the compare-to-JSON lede
  • More consistently apply POLA terminology

@sonnyp
Copy link
Contributor

sonnyp commented May 6, 2015

There are many other edge cases.

JSON.stringify discard undefined values in objects, not in arrays where they are turned into null.

JSON.stringify({foo: undefined}) returns {}
JSON.stringify([true, undefined, true]) returns [true,null,true]

Same thing for ES6 symbols
Also NaN, Infinity, -Infinity, +Infinity but in an object contrary to undefined and symbols the key is kept and the value converted to null)

And there is the special case where the value is an object and replaced with the result of its toJSON method if defined.

also
JSON.stringify([,,]) returns [null, null]

and pretty much the same hings if an object key isn't JSON valid.

I think we should provide a sanitize function that runs the patch and/or the target document through JSON.stringify/JSON.parse and recommend using it before evaluation.

@warpech
Copy link
Collaborator

warpech commented Oct 19, 2015

Labelling this as "question". This lib will always prioritise performance over sanity-checking. But perhaps something can be done to improve the developer UX.

Currently the explanation in README.md is not very helping (written in strikethrough):

screenshot 2015-10-19 16 39 09

warpech added a commit that referenced this issue Jun 3, 2016
this is a fix for issues #105, #90, #60, #32 and a part of #76 that describes a problem with undefined
all tests pass after this commit
@warpech
Copy link
Collaborator

warpech commented Jun 21, 2016

Fixed in 1.0.0

@warpech warpech closed this as completed Jun 21, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants