Serialize scenegraph data to JSON?

I was wondering if there was a way to serialize the scenegraph nodes and properties to JSON?

1 Like

There is an open-source library (MIT-licensed) by “@svschannak” (GitHub) which can do that:

It is also available via npm:

Hope this helps,


@stevekwak, is it possible to convert this topic to feature request? Suggested library seems to be an overhead way to convert objects tree into json (e.g. if properties list change library should be adjusted as well).
As quick fix it might be:

  • make properties enumerable;
  • adjust types (by some reason typeof removeFromParent is object).

In general it will be great to have toJson method which will do all the job in async manner.

For now serializing of default document (which goes with xd to show functionality) to json takes 118 secs and make xd unresponsive. Is there any way to call it async right now? And it’s just 9 artboards. How it will work if it will be 70 artboards with much more complex layout?
As the next step I need to export artboards previews and zip them. I’m afraid all these operations will put Xd on freeze for a much longer time.
UPD: It seems that the slowest operation in my case was console.log. I dropped mostly all of them and time reduced to couple seconds :slight_smile:

My attempt to convert scenegraph root object to json:

Really useful :slight_smile: Although it doesn’t seem to extract Bounds and Points properly

I changed line 16 to

const keys = Object.keys(value).concat(Object.keys(keyDescriptors));

to temporarily fix that

1 Like

Ah I spoke too soon - that line crashes XD for certain nodes

It crashes XD? Or just the plugin?

haha the whole of XD :wink: I think I’ve figured out why - it causes some kind of infinite loop, so I’m guessing XD notices and kills it.

Holy Crap!!! That shouldn’t happen, that’s for sure!

OK I’ve improved the code a bit to prevent the loops - it now only considers plain types from Object.keys which I think is what we want. I added the following at line 43:

    // also pull any plain types from Object.keys - be careful of initite loops!
    for(const key of Object.keys(value)){
      if (skipFields.indexOf(key) > -1 || key[0] === "_" || !isPlainType(value[key])) {
      json[key] = value[key];

@kerrishotts I have made the recursive crash code into a reproducible example. This plugin consistently crashes XD each time I run it.

1 Like

Thank you! Very good point! I made slightly different fix based on the points of inheritance and magic XD crashes. I just do not iterate over elements which extends Objects or something weird called “” (empty string). So plain type check now looks like:

return (!value
  || ["object", "function"].indexOf(typeof value) === -1
  || value instanceof Date
  || ["Object", ""].indexOf(Object.getPrototypeOf(value) > -1);

updated the gist.

Won’t that still fail to expand Bounds and Points? I think you might still need to iterate Object.keys(value) instead of just the prototype keys.

You are completely right! I made another attempt by including Object.keys into getDescriptors as value might be complex object (e.g. color stops for linear gradient) and will be filtered out by isPlainType.

That seems to work mostly, but I’ve found that line 80 can now crash if descriptor is null.

Fixed it by changing it to

: !descriptor || typeof (descriptor.value === "undefined") ? entity[key] : descriptor.value;

Hmm actually it’s still not going deep enough into color stops in a linear gradient


update - it’s because colorStops[0] is classed as a “plain” type

I removed your new plain type thing (line 72) and then tracked down the infinite loop. It’s in the key triggeredInteractions, so I added that to skipFields. Seems to be running OK now, until we find the next problem :stuck_out_tongue:

edit: I’ve added incomingInteractions and selected to skipFields too. How would you feel about open sourcing this to github/npm @gdreyv? Just so we don’t have to post here every time we find something :stuck_out_tongue: I’m happy to host on if you don’t have time to put it up

1 Like

I just thought that opensourcing a single file which is temporary solution doesn’t make any sense :slight_smile: Anyway I made several changed to dive into interactions (as the idea to have full mapping) and published draft here: but I will try to keep gist up to date as well.

What about selected field? How can I reproduce it? The point about interaction is to replace real nodes with references to keep data but prevent infinite loops.

What’s wrong with .selected? Isn’t it just a bool?