Rohit Desai
Rohit Desai

Reputation: 1

How to handle circular references in TypeScript when serializing objects to include `$ref` annotations

I have an object structure where circular references need to be represented using $ref annotations in the output. Here is an example of the input and expected output :-

Input is -

const obj2 = { circle: {} };    obj2.circle = obj2; 
const obj3 = { foo: 'bar' }; 
const obj = { 
  obj3A: obj3,
  obj3B: obj3,
  paths: { 
      '/circle': {
           obj2: {}
      }, }, }; 
obj.paths['/circle'].obj2 = obj2; 

EXPECTED OUTPUT -

obj3A: { "foo": "bar" }, 
obj3B: { "foo": "bar" },
paths: { 
   "/circle": { 
       obj2: { 
         circle: { 
           $ref:"#/paths/~1circle/obj2" } } } } } 

obj2 contains a circular reference to itself, represented by $ref. How can I implement a TypeScript function to achieve this output when serializing objects? for repeated value and non circular references should represent with value as I shown I output and for circular references in shows like reference

My solution

export function decycle(obj: unknown, replacer?: (value: any) => any) {
  const objs = new weakmap<object, string>();
  function derez(value: any, path: (string | number)[]): any {
    if (replacer) {
      value = replacer(value);
    }
    if (typeof value === 'object' && value !== null) {
      const oldPath = objs.get(value);
      if (oldPath) {
        return { $ref: oldPath };
      }
      objs.set(value, pathToPointer(path));
      if (Array.isArray(value)) {
        return value.map((element, i) => derez(element, [...path, i]));
      }
      const newObj: Record<string, any> = {};
      for (const name in value) {
        if (Object.prototype.hasOwnProperty.call(value, name)) {
          newObj[name] = derez(value[name], [...path, name]);
        }
      }
      return newObj;
    }
    return value;
  }
  return derez(obj, []);
}

Upvotes: 0

Views: 41

Answers (0)

Related Questions