Starcounter-Jack / JSON-Patch

Lean and mean Javascript implementation of the JSON-Patch standard (RFC 6902). Update JSON documents using delta patches.
MIT License
1.78k stars 215 forks source link

Attempting to use if mirror has a toJSON and object does not breaks #318

Open NoLongerBreathedIn opened 10 months ago

NoLongerBreathedIn commented 10 months ago

For example, suppose you have the following objects:

const object = {
    bug: new Date('2032-01-04'),
    nobug: new Date('2032-01-04')
};
const mirror = {
    bug: new Date('2032-01-04'),
    nobug: new Date('2032-01-04').toJSON()
};

Then compare(mirror, obj); returns the following array:


[{
    op: "replace",
    path: "/nobug",
    value: "2032-01-04T00:00:00.000Z"
}, {
    op: "add",
    path: "/bug/0",
    value: "2"
}, {
    op: "add",
    path: "/bug/1",
    value: "0"
}, {
    op: "add",
    path: "/bug/2",
    value: "3"
}, {
    op: "add",
    path: "/bug/3",
    value: "2"
}, {
    op: "add",
    path: "/bug/4",
    value: "-"
}, {
    op: "add",
    path: "/bug/5",
    value: "0"
}, {
    op: "add",
    path: "/bug/6",
    value: "1"
}, {
    op: "add",
    path: "/bug/7",
    value: "-"
}, {
    op: "add",
    path: "/bug/8",
    value: "0"
}, {
    op: "add",
    path: "/bug/9",
    value: "4"
}, {
    op: "add",
    path: "/bug/10",
    value: "T"
}, {
    op: "add",
    path: "/bug/11",
    value: "0"
}, {
    op: "add",
    path: "/bug/12",
    value: "0"
}, {
    op: "add",
    path: "/bug/13",
    value: ":"
}, {
    op: "add",
    path: "/bug/14",
    value: "0"
}, {
    op: "add",
    path: "/bug/15",
    value: "0"
}, {
    op: "add",
    path: "/bug/16",
    value: ":"
}, {
    op: "add",
    path: "/bug/17",
    value: "0"
}, {
    op: "add",
    path: "/bug/18",
    value: "0"
}, {
    op: "add",
    path: "/bug/19",
    value: "."
}, {
    op: "add",
    path: "/bug/20",
    value: "0"
}, {
    op: "add",
    path: "/bug/21",
    value: "0"
}, {
    op: "add",
    path: "/bug/22",
    value: "0"
}, {
    op: "add",
    path: "/bug/23",
    value: "Z"
}]
begrs commented 10 months ago

This issue persists since 2021 which is really bad for such a far reaching library. We have the same issue. It happens when both objects contain a Date. Duplicate of #278

Workaround: when creating the objects to compare, use toIsoString beforehand. e.g. bug = new Date('2032-01-04').toIsoString();

Error Source: // src/duplex.ts line 148 to 149 if (typeof obj.toJSON === "function") { obj = obj.toJSON(); } Explanation:

Possible Solution:

bohoffi commented 8 months ago

Here's a little helper function which patches Date values - it traverses over arrays and objects:

/**
 * The function takes an input value and applies data type patches if the input value is:
 * - `Date` --> JSON representation as fast-json-patch cannot handle Dates
 * - an `object` --> traverses the object tree applying patches to properties where necessary
 * - an `array` --> traverses the array elements applying patches to properties where necessary
 *
 * All other data types - e.g. `string`, `number` will stay unpatched.
 *
 * @returns patched input when necessary
 */
export function patchDataTypes<T>(input: T): any {
  if (input instanceof Date) {
    return input.toJSON() as string;
  } else if (typeof input === 'object' && input !== null) {
    if (Array.isArray(input)) {
      // If it's an array, recursively convert each element
      return input.map(patchDataTypes);
    } else {
      // If it's an object, recursively convert each property
      const result: Record<string, unknown> = {};
      for (const key in input) {
        result[key] = patchDataTypes(input[key]);
      }
      return result;
    }
  } else {
    return input;
  }
}