Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Script Help] Using GM Notes to import JSON Array

Hi folks!  I have hit a wall in my understanding of how to manipulate these fields and my Google-Fu has failed me.  HeroLab Online has finally made it so that you can export a character as a JSON file , which looks to me just like a big ol' nested array which I thought I knew how to manipulate. However, my problem is I can't figure out how to get that information into the Roll20 API.  I thought that I could copy the text into the GM Notes and then access that by getting it from the character pushing it into an array. I understand that I need a callback function to access that, but I am confused about how to make that text into an array. I've tried JSON.parse(), .push, and just setting an empty array equal to the field. I'm obviously missing something. Here is the piece I think I need:  //selected token  var token = getObj("graphic",selected[0]._id); var character = getObj("character",token.get("represents")); //JSON in GM Notes var herolabData=[]; character.get("gmnotes", function(gmnotes) { herolabData = gmnotes; }); Then I also noticed that a lot of the entries had dots in the actual fields which made me doubt knowing how to access a particular entry. For instance: "actors":{ "actor.1":{ "name":"ellewwelle", "player":"aaron", "gameValues":{ "actXPNet":11010, "actLevelNet":12, "actAlignment":"Neutral Good", "actRace":"elf", "actBackgroundText":"Acolyte", "actClassText":"cleric 12", "actBulk":2.6,"actEncumbered":5, "actOverburdened":10, "actSpace":5, "actReach":5, "actSocietyChar":2001, "Trait":"trtElf,trtGood,trtHumanoid,trtNeutralLC" }, If I needed the level to place into the sheet, I would need to call actors.actor.1.gamevalues.get("actXPNet")? But wouldn't the .1 throw things off? I think I've burnt my brain here somewhere and I'm missing something simple.  Thanks!
1608080005
Jordan C.
Pro
API Scripter
For the keys that have dots in them you need the [" key "] syntax. That is to say in your example you would need the following: actors["actor.1"].gameValues.actXPNet Also I would run your JSON through a JSON linter of some sort to be sure it is able to be processed correctly. For instance, unless that wasn't the full text of the JSON you would need to adjust it the the following: { "actors": { "actor.1": { "name": "ellewwelle", "player": "aaron", "gameValues": { "actXPNet": 11010, "actLevelNet": 12, "actAlignment": "Neutral Good", "actRace": "elf", "actBackgroundText": "Acolyte", "actClassText": "cleric 12", "actBulk": 2.6, "actEncumbered": 5, "actOverburdened": 10, "actSpace": 5, "actReach": 5, "actSocietyChar": 2001, "Trait": "trtElf,trtGood,trtHumanoid,trtNeutralLC" } } } }
1608082363
The Aaron
Roll20 Production Team
API Scripter
Also, you likely want to parse the JSON like this: //selected token var token = getObj("graphic",selected[0]._id); var character = getObj("character",token.get("represents")); //JSON in GM Notes var herolabData=[]; character.get("gmnotes", function(gmnotes) { herolabData = JSON.parse( gmnotes ) ; });
Jordan C. said: For the keys that have dots in them you need the [" key "] syntax. That is to say in your example you would need the following: actors["actor.1"].gameValues.actXPNet Also I would run your JSON through a JSON linter of some sort to be sure it is able to be processed correctly. For instance, unless that wasn't the full text of the JSON you would need to adjust it the the following: { "actors": { "actor.1": { "name": "ellewwelle", "player": "aaron", "gameValues": { "actXPNet": 11010, "actLevelNet": 12, "actAlignment": "Neutral Good", "actRace": "elf", "actBackgroundText": "Acolyte", "actClassText": "cleric 12", "actBulk": 2.6, "actEncumbered": 5, "actOverburdened": 10, "actSpace": 5, "actReach": 5, "actSocietyChar": 2001, "Trait": "trtElf,trtGood,trtHumanoid,trtNeutralLC" } } } } Ahh, I have not learned about that syntax yet. Thanks! The full JSON is (probably?) properly formatted. That was just a small chunk of it.  The Aaron said: Also, you likely want to parse the JSON like this: //selected token var token = getObj("graphic",selected[0]._id); var character = getObj("character",token.get("represents")); //JSON in GM Notes var herolabData=[]; character.get("gmnotes", function(gmnotes) { herolabData = JSON.parse( gmnotes ) ; }); Hmm, I tried adding that in and got an error:  SyntaxError: Unexpected token < in JSON at position 0 SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at apiscript.js:19:23 at /home/node/d20-api-server/api.js:828:7 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Rd.Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:111:400 at S (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:57:1015)
1608139216

Edited 1608139329
Jordan C.
Pro
API Scripter
You need the following: const strip = /<[^>]*>/g; gmnotes = gmnotes.replace(strip, ''); You are grabbing pure innerHTML which makes your JSON appear like this: <p>{actors...}</p> So stripping the html returns a valid JSON string again. The < at position 0 is a strong indicator that your string starts with an html tag
Ok, so when I paste it into the GM Notes, Roll20 adds HTML formatting for looks. Then that gets passed along to the API as well. Makes sense.  So now I've pasted the following into the gmnotes of a blank character: { "actors":{ "actor.1":{ "name":"ellewwelle", "player":"aaron", "gameValues":{ "actXPNet":11010, "actLevelNet":12, "actAlignment":"Neutral Good", "actRace":"elf", "actBackgroundText":"Acolyte", "actClassText":"cleric 12", "actBulk":2.6, "actEncumbered":5, "actOverburdened":10, "actSpace":5, "actReach":5, "actSocietyChar":2001, "Trait":"trtElf,trtGood,trtHumanoid,trtNeutralLC" } } } } And this is the relevant portion of the script:  //JSON in GM Notes         var herolabData = []; character.get("gmnotes", function(gmnotes) {     const strip = /<[^>]*>/g;     gmnotes = gmnotes.replace(strip, ''); herolabData = JSON.parse(gmnotes); });         log(herolabData);                  sendChat("HLO Import", `${herolabData.actors["actor.1"].gameValues.actXPNet}`); The log is just showing a blank array [] and the console is throwing an undefined error.  TypeError: Cannot read property 'actor.1' of undefined Do I need to do any additional formatting of the notes for it to be properly put into the array?
1608148882
The Aaron
Roll20 Production Team
API Scripter
ah.  Welcome to Asynchronous programming. Since .get() on character for GMNotes is asynchronous, the execution order is conceptually this: //JSON in GM Notes         var herolabData = []; character.get("gmnotes", /* <ASYNC DELAY HERE> */ );         log(herolabData);                  sendChat("HLO Import", `${herolabData.actors["actor.1"].gameValues.actXPNet}`);         // <ASYNC RETURNS HERE>         function(gmnotes) { const strip = /<[^>]*>/g; gmnotes = gmnotes.replace(strip, ''); herolabData = JSON.parse(gmnotes); } You'll need to place your additional operations in a function or "pay it forward" in some other manner.  Something like: //JSON in GM Notes const toDo = (jsonData) => { log(herolabData); sendChat("HLO Import", `${herolabData.actors["actor.1"].gameValues.actXPNet}`); }; character.get("gmnotes", function(gmnotes) { const strip = /<[^>]*>/g; gmnotes = gmnotes.replace(strip, ''); toDo (JSON.parse(gmnotes)); });
1608148937
The Aaron
Roll20 Production Team
API Scripter
More details on Asynchronous functions here:&nbsp;<a href="https://wiki.roll20.net/API:Use_Guide#A_Treatise_on_Asynchronous_Functions" rel="nofollow">https://wiki.roll20.net/API:Use_Guide#A_Treatise_on_Asynchronous_Functions</a>
Is this similar to what we did to bring information from HLC to the Companion sheet for PF1? I recall having to run my JSON output through Notepad and doing some editing to make it work. It still did not pull everything from HLC though. I have no idea how similar the code is between HLC and HLO though. I do know I am really interested in getting this to work right. I am seeing a lot of people shifting their VTT preference because they can import characters pretty easily from HLO to that other VTT, not to mention how fast they can a brand new table up and running on it.
The Aaron said: More details on Asynchronous functions here:&nbsp; <a href="https://wiki.roll20.net/API:Use_Guide#A_Treatise_on_Asynchronous_Functions" rel="nofollow">https://wiki.roll20.net/API:Use_Guide#A_Treatise_on_Asynchronous_Functions</a> Aaron, You have broken my faith in reality and in the linear nature of cause and effect. I'm going to have to read these a few times.&nbsp; Xathos of Varisia said: Is this similar to what we did to bring information from HLC to the Companion sheet for PF1? I recall having to run my JSON output through Notepad and doing some editing to make it work. It still did not pull everything from HLC though. I have no idea how similar the code is between HLC and HLO though. I do know I am really interested in getting this to work right. I am seeing a lot of people shifting their VTT preference because they can import characters pretty easily from HLO to that other VTT, not to mention how fast they can a brand new table up and running on it. It may be? HLO just released it and it doesn't seem like it has everything yet. It's still missing a couple things but nothing major. I've linked the JSON up in my first post if you want to take a look through it.&nbsp;
1608156231
The Aaron
Roll20 Production Team
API Scripter
It can absolutely be a bit confusing at first if you've never had to deal with it before.
Yes, yes it is. I think I've got enough to get to real work now. Thanks for all the help, Aaron and Jordan! &nbsp; &nbsp; &nbsp; //JSON in GM Notes &nbsp; &nbsp; &nbsp; &nbsp; const toDo = (herolabData) =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var ancestry = findObjs({type: 'attribute', characterid: character.id, name: 'ancestry_heritage'})[0]; &nbsp; &nbsp; &nbsp; &nbsp; if (!ancestry) { &nbsp; &nbsp; &nbsp; &nbsp; createObj('attribute', { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name :"ancestry_heritage",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current: toProperCase(herolabData.actors["actor.1"].gameValues.actRace), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: character.id &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ancestry.set('current', toProperCase(herolabData.actors["actor.1"].gameValues.actRace));&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp; &nbsp; &nbsp; var alignment = findObjs({type: 'attribute', characterid: character.id, name: 'alignment'})[0]; &nbsp; &nbsp; &nbsp; &nbsp; if (!alignment) { &nbsp; &nbsp; &nbsp; &nbsp; createObj('attribute', { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name :"alignment",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current: toProperCase(herolabData.actors["actor.1"].gameValues.actAlignment), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: character.id &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alignment.set('current', toProperCase(herolabData.actors["actor.1"].gameValues.actAlignment));&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("HLO Import", `Race has been set`); &nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; character.get("gmnotes", function(gmnotes) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const strip = /&lt;[^&gt;]*&gt;/g; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes = gmnotes.replace(strip, ''); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; toDo(JSON.parse(gmnotes)); &nbsp; &nbsp; &nbsp; &nbsp; }); Now I've just got to go through the JSON and start matching up attribute names to array locations. Is there a way to simulate drag and drop from the compendium through the API or am I stuck populating every single repeating row for items and spells?
1608167623
The Aaron
Roll20 Production Team
API Scripter
I believe you'll have to do it manually.
Well, blerg. Thanks anyway!