Copy link to clipboard
Copied
Hello again,
JSON is good for saving presets. However, I am looking for an easy way to bring JSON and the UI variables together.
My project: I have over 50 JSON entries in a javascript project for Illustrator that need to be loaded, changed and saved again in a UI.
It is very annoying to have to call a function like 'function fJSON2UI()' every time a change is made, which transfers the values from JSON to the variables for the UI and back again.
The wish: It would be simpler if I could write in the UI:
JO.uiFile = gInput1.add ("statictext", undefined, "(no file)");
But this does not work.
QUESTION: Is there a trick or shortcut to connect the JSON and the variables of the UI more easily?
Part of my Javascript:
var vJSON = {
uiFile: "~/Desktop/"
};
var w = new Window ('dialog {text: "Preferences", orientation: "column", alignChildren:["fill","fill"], properties: {closeButton: false}}');
gInput1 = w.add ('group {orientation: "row"}'); gInput1.alignment = "left";
bInput1 = gInput1.add ("button", undefined, "Load");
vInput1 = gInput1.add ("statictext", undefined, "(no file)"); vInput1.characters = 40;
bInput2 = gInput1.add ("button", undefined, "Save");
bInput1.onClick = function () {
vResult = File.openDialog( "Select a JSON-file:", "JSON:*.json" );
if (vResult != null) { vInput1.text = vResult; fReadJSON(vResult);
fJSON2UI();
}
else { vInput1.text = "(keine Datei gewählt)"; vResult = null; }
bInput1.active = false; // reset button
} // end of bInput1.onClick function ()
bInput2.onClick = function () {
if (vInput1.text != "(keine Datei gewählt)") {
vFile = new File(vResult); vResultSave = vFile.saveDlg( "Save as JSON-file:", "JSON:*.json" ); }
else { vResultSave = File.saveDialog( "Save as JSON-file:", "JSON:*.json" ); }
if (vResultSave != null) { vInput1.text = vResultSave; fWriteJSON(vResultSave);
fUI2JSON();
}
bInput2.active = false; // reset button
} // end of bInput2.onClick function ()
w.show();
function fReadJSON(pFile){ } // read the JSON file
function fWriteJSON(pFile){ } // write the JSON file
function fJSON2UI() {
vInput1.text = vJSON.uiFile; // and 49 variables more
}
function fUI2JSON() {
vJSON.uiFile = vInput1.text; // and 49 variables more
}
Looking forward to your help,
– j.
Thanks for your help.
Here is my solution. It works in AI v26.0.3.
var JO = { JSname: "Plus", JSversion: "1.0", uiProject: "Mustermann", uiPrintFormat: "ai", uiCuttingFormat: "pdf", uiPrintSaveFolder: "~/Desktop/", uiPrintSave: true, uiCuttingSave: true, uiPageSize: "A4", uiGridSheetWidth: 210.0, uiGridSheetHeight: 297.0, uiPageMarginLeft: 7.0, uiPageMarginRight: 7.0, uiPageMarginTop: 7.0, uiPageMarginBottom: 7.0, uiShowPageMargin: true, }; // the JSON object
var aUI = new Array; // the values for
...
Copy link to clipboard
Copied
You can only do something like this:
Create an object literal or class which can be used to help describe each one of the 50 variables.
var JsonObjTemplate = {
name : {
name : "name", // helps to locate this object if it ends up being inside an array & not this template for some reason at run-time.
type : "string", // This is a fundamental necessary property which helps with what sort of UI control to produce.
defaultValue : "ABCD", // a property that helps future operations do something unique.
},
age : {
name : "age",
type : "number",
defaultValue : 0,
},
};
Create a method or a bunch of methods which can operate on the data template object and show the variables as some sort of UI controls.
One way to do this is to have not a lot of separate sections but to have one listbox which can hold an unlimited number of items and then another area such as group or panel which has orientation = "stack" and different UI controls can be hidden or shown based on the selection in the listbox.
Another way is to maybe not have a listbox and have any arbitrary UI you please.
Here's an example of a method that can output a control based on the data template.
If some code isn't working due to a need for a polyfill such as JSON object, please obtain any polyfills you might need:
function getUIControl (typeDescObj, parent) {
var type = typeDescObj.type;
var name = typeDescObj.name;
var control = null;
return control;
}
Then you'd make a window, run the variables template through a loop and add all the controls you would need.
You may want to create a method for getting all data from the UI into a JSON object and also for setting the data to an existing UI window.
It is possible to add your own custom prototypes to ScriptUI widgets such as 'edittext'.
EditText.prototype.setValue = function (val) {
this.text = val;
};
With such prototypes you can add additional arguments, or you can add additional properties to instantiated EditText objects which can be used by the prototype getValue/setValue methods to do more than just `this.text = ""`.
For example, assuming a second argument of "typeDescObj" is passed in. Before setting a text value, this prototype method could now check for the 'type' property or any other properties and do such manipulations as math rounding or replacing unwanted characters with an underscore '_'.
The advantage of using a prototype is that throughout the code you can always rely on your EditText(s) and StaticText(s) being able to get and set values without having to paste a method everywhere. However, it doesn't have to be a prototype, it can be just a regular function that you do paste everywhere and there's nothing wrong with that.
It can be a little bit of work, but if you do this then for all future time you'll be able to make verbose UIs for many differents data objects - as long as your code supports those properties. So if you are doing a new script that wants a new data type, all you do is add the new block in your code library and now it's enabled in all of your scripts!
Here is an example of my own code which uses these techniques. I make a settings menu out of this which doesn't use any listbox or dynamic display area per row, it just adds sections to one dialog box. The objective for this was never to have more than 10 or so settings variables. If it were to increase, I would make it into a listbox for sure. One can also go all the way with this 'architecture' by creating nested controls which are also dynamic like so. Your control object isn't an EditText, it's a Group or Panel which has its own "setValue" and "getValue", and those can wrap the same structure meaning it can have nested EditTexts and StaticTexts and other Group or Panel on and on.
Copy link to clipboard
Copied
Thank you very much for your answer. Sounds to complicated to my, because I have a very simple, linear JSON of string and booleans. So I guess the fastes way to put JSON into the variables and back again is just a long list in two functions like this:
function fJSONtoUI() {
vInput1.value = JO.uiAutoSave;
vInput2.text = JO.uiFileName;
// more to come
}
function fUItoJSON() {
JO.uiAutoSave = vInput1.value;
JO.uiFileName = vInput2.text;
// more to come
}
Copy link to clipboard
Copied
Don't think of it as putting something back into "JSON", it's simply putting all the items into an object. That object can be written as JSON or anything else. So, what you are really after is loops and properties that can assign values from UI widgets to the object. If you have a fJSONtoUI() method with 100 lines of x.text = "ABC", that's a valid way to get to your goal surely.
Copy link to clipboard
Copied
I guess, I found a way (quick and dirty):
var JO = { JSname: "Plus", JSversion: "1.0", uiProject: "Mustermann", uiPrintFormat: "ai", uiCuttingFormat: "pdf", uiPrintSaveFolder: "~/Desktop/", uiPrintSave: true, uiCuttingSave: true, uiPageSize: "A4", uiGridSheetWidth: 210.0, uiGridSheetHeight: 297.0, uiPageMarginLeft: 7.0, uiPageMarginRight: 7.0, uiPageMarginTop: 7.0, uiPageMarginBottom: 7.0, uiShowPageMargin: true, }; // the JSON object
var aUI = new Array; // the values for the UI
var aJSONkeys = new Array; // the JSON keys
var vSplit;
function fJsonString(pObject) { // ObjectToString
var vString = ""+ (pObject.toSource());
return (vString.substring(1, vString.lastIndexOf(")"))); // tip: return without ( ... ) at start and end
} // end-of-function
function fJsonParse(pString) { // String to Object
return Function('"use strict";return (' + pString + ')')();
} // end-of-function
function fJSONtoUI(pJSON) { // convert values of a linear JSON to a linear array
var vJString = fJsonString(pJSON);
if (vJString.indexOf(", ") > -1) { vSplit = ", "; } else { vSplit=","; } // comma + space or just comma
vJString = vJString.substring(1, vJString.lastIndexOf("}"));
var aArray = vJString.split(vSplit)
for (x=0; x < aArray.length; x++){ var aRow = aArray[x].split(":"); aJSONkeys.push(aRow[0]); aUI.push(aRow[1]); }
return aUI;
}
function fUItoJSON(pArray) {
var vString = "{";
for (x=0; x < pArray.length; x++){ vString += aJSONkeys[x] + ":" + aUI[x] + vSplit; }
vString = vString.substring(0, vString.lastIndexOf(",")) + "}";
return fJsonParse(vString);
}
// main script - part one
aUI = fJSONtoUI(JO); // run this function first
alert("length of array: " + aUI.length + "\nfirst value: " + aUI[0]); // for testing
// - part two
JO = fUItoJSON(aUI);
alert(fJsonString(JO)); // for testing
// end of javascript
What do you think about it?
– j.
Copy link to clipboard
Copied
There's already a JSON object. Just simply paste this and use it, it's worked for me for years.
The JSON object and its methods are an industry standard thing, so its behavior can be relied on for an expected output all the time. I used to not like having this JSON object when we have .toSource(), but really soon as I began to work with other data such as other people's data, it became the only thing that made sense.
You use it by taking your object and doing JSON.stringify(obj, null, 2) and it will give you a nice indented printout.
You can further manipulate this printout by splitting it by nextlines, slicing all but 1st and last items (the parentheses) to take out the {} enclosing parentheses. And you can format the things anywhich way you wish by going over the split-by-nextlines array of the JSON string printout and doing string-editing on those lines.
"object" != typeof JSON && (JSON = {}), function () {
"use strict";
function f(t) { return 10 > t ? "0" + t : t; }
function quote(t) {
return escapable.lastIndex = 0, escapable.test(t) ? '"' + t.replace(escapable, function (t) {
var e = meta[t];
return "string" == typeof e ? e : "\\u" + ("0000" + t.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + t + '"';
}
function str(t, e) {
var n, r, o, f, u, i = gap, p = e[t];
switch ((p && "object" == typeof p && "function" == typeof p.toJSON && (p = p.toJSON(t)),
"function" == typeof rep && (p = rep.call(e, t, p)), typeof p)) {
case "string": return quote(p);
case "number": return isFinite(p) ? String(p) : "null";
case "boolean":
case "null": return String(p);
case "object":
if (!p)
return "null";
if (gap += indent, u = [], "[object Array]" === Object.prototype.toString.apply(p)) {
for (f = p.length, n = 0; f > n; n += 1)
u[n] = str(n, p) || "null";
return o = 0 === u.length ? "[]" : gap ? "[\n" + gap + u.join(",\n" + gap) + "\n" + i + "]" : "[" + u.join(",") + "]", gap = i, o;
}
if (rep && "object" == typeof rep)
for (f = rep.length, n = 0; f > n; n += 1)
"string" == typeof rep[n] && (r = rep[n], o = str(r, p), o && u.push(quote(r) + (gap ? ": " : ":") + o));
else
for (r in p)
Object.prototype.hasOwnProperty.call(p, r) && (o = str(r, p), o && u.push(quote(r) + (gap ? ": " : ":") + o));
return o = 0 === u.length ? "{}" : gap ? "{\n" + gap +
u.join(",\n" + gap) + "\n" + i + "}" : "{" + u.join(",") + "}", gap = i, o;
}
}
"function" != typeof Date.prototype.toJSON && (Date.prototype.toJSON = function () {
return isFinite(this.valueOf()) ? this.getUTCFullYear() + "-" + f(this.getUTCMonth() + 1) + "-" + f(this.getUTCDate()) + "T" + f(this.getUTCHours()) + ":" +
f(this.getUTCMinutes()) + ":" + f(this.getUTCSeconds()) + "Z" : null;
}, String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function () {
return this.valueOf();
});
var cx, escapable, gap, indent, meta, rep;
"function" != typeof JSON.stringify &&
(escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { "\b": "\\b", " ": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", '"': '\\"', "\\": "\\\\" }, JSON.stringify = function (t, e, n) {
var r;
if (gap = "", indent = "", "number" == typeof n)
for (r = 0; n > r; r += 1)
indent += " ";
else
"string" == typeof n && (indent = n);
if (rep = e,
e && "function" != typeof e && ("object" != typeof e || "number" != typeof e.length))
throw new Error("JSON.stringify");
return str("", { "": t });
}),
"function" != typeof JSON.parse && (cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
JSON.parse = function (text, reviver) {
function walk(t, e) {
var n, r, o = t[e];
if (o && "object" == typeof o)
for (n in o)
Object.prototype.hasOwnProperty.call(o, n) &&
(r = walk(o, n), void 0 !== r ? o[n] = r : delete o[n]);
return reviver.call(t, e, o);
}
var j;
if (text = String(text), cx.lastIndex = 0, cx.test(text) &&
(text = text.replace(cx, function (t) { return "\\u" + ("0000" + t.charCodeAt(0).toString(16)).slice(-4); })),
/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")))
return j = eval("(" + text + ")"), "function" == typeof reviver ? walk({ "": j }, "") : j;
throw new SyntaxError("JSON.parse");
});
}();
This object is something that one can keep in a library jsx file and always include it in other scripts with #include.
Your method unfortunately has the vulnerability despite the "use strict":
var result = fJsonParse("eval(\'alert(\"AAA!\")\')");
Doing same with JSON.parse will throw a JSON.parse Syntax Error.
Copy link to clipboard
Copied
to my code above: A bit too dirty. Because the properties like .text and .value are missing for the UI.
Let's see what I can find as a solution tomorrow.
to your code:
Thank you. The code looks wild and frankly I don't understand a line of it.
However, I am not interested in formatting JSON. Mine is quite simple, linear, as shown above.
Mine is about getting the JSON data into a dialog (UI) and putting the fields from the dialog back into the JSON.
I have managed a conversion from JSON to a simple array and back. What's missing now are the properties in the array, so e.g. aUI[3].text, so that I can call something like
aUI[3] = gInput4.add ("edittext", undefined, "(no name)"); // wrong, it's not working this way
Will have to continue with this tomorrow.
Copy link to clipboard
Copied
I think you should focus on 3 methods which are 1) create the UI, 2) set data to UI, 3) get data from UI.
Now assuming there's a javascript object like this:
var x = {
prop_1 : "ABC",
prop_2 : 123,
};
You can create the UI using a loop and push the UI items to a data object to store for reference.
var uiItems = {};
for (var all in x) {
uiItems[all] = someParentGroup.add("edittext", undefined, "");
uiItems[all].characters = 10; // some default width to the box.
}
Then you can use another block of code to form another method with that can set the values:
for (var all in x) {
uiItems[all].text = x[all];
}
You can use the uiItems object to create a method which gets the data:
var result = {};
for (var all in x) {
result[all] = uiItems[all].text;
}
Copy link to clipboard
Copied
Thanks for your help.
Here is my solution. It works in AI v26.0.3.
var JO = { JSname: "Plus", JSversion: "1.0", uiProject: "Mustermann", uiPrintFormat: "ai", uiCuttingFormat: "pdf", uiPrintSaveFolder: "~/Desktop/", uiPrintSave: true, uiCuttingSave: true, uiPageSize: "A4", uiGridSheetWidth: 210.0, uiGridSheetHeight: 297.0, uiPageMarginLeft: 7.0, uiPageMarginRight: 7.0, uiPageMarginTop: 7.0, uiPageMarginBottom: 7.0, uiShowPageMargin: true, }; // the JSON object
var aUI = new Array; // the values for the UI
aUI.text = ""; aUI.value;
var aJSONkeys = new Array; // the JSON keys
var vSplit; // split the JSON with comma + space or comma only
function fJsonString(pObject) { // ObjectToString
var vString = ""+ (pObject.toSource());
return fDeleteFL(vString,")"); // tip: return without ( ... ) at start and end
} // end-of-function
function fJsonParse(pString) { // String to Object
return Function('"use strict";return (' + pString + ')')();
} // end-of-function
function fDeleteFL(pString,pLastChar) { // delete the first and the last char of a string
return pString.substring(1, pString.lastIndexOf(pLastChar));
}
var vQuote = String.fromCharCode(34);
function fJSONtoUI(pJSON) { // convert values of a linear JSON to a linear array
var vJString = fJsonString(pJSON);
if (vJString.indexOf(", ") > -1) { vSplit = ", "; } else { vSplit=","; } // comma + space or just comma
vJString = fDeleteFL(vJString,"}");
var aArray = vJString.split(vSplit)
for (x=0; x < aArray.length; x++){
var aRow = aArray[x].split(":");
aJSONkeys.push(aRow[0]);
if (aRow[1].indexOf(vQuote) > -1) {aUI.push({text:aRow[1]});}
else {aUI.push({value:aRow[1]});}
}
return aUI;
}
function fUItoJSON(pArray) {
var vString = "{";
for (x=0; x < pArray.length; x++){
if (aUI[x].text != undefined) {
vValue = aUI[x].text;
if (vValue.indexOf(vQuote) != 0) { vValue = vQuote+vValue+vQuote;} // add the quotes again
}
else { vValue = aUI[x].value; }
vString += aJSONkeys[x] + ":" + vValue + vSplit;
}
vString = vString.substring(0, vString.lastIndexOf(",")) + "}";
return fJsonParse(vString);
}
// ---- main script ----
aUI = fJSONtoUI(JO); // run this function first
// Test with a small UI
var myWindow = new Window ("dialog", "Form");
myWindow.add ("statictext", undefined, "0. Name:");
aUI[0] = myWindow.add ("edittext", undefined, fDeleteFL(aUI[0].text,vQuote));
aUI[0].characters = 30; aUI[0].active = true;
myWindow.add ("statictext", undefined, "10. Number:");
aUI[10] = myWindow.add ("edittext", undefined, aUI[10].value);
aUI[10].characters = 30;
myWindow.add ("statictext", undefined, "7. Checkbox:");
vC7 = myWindow.add ("checkbox", undefined, "show it"); vC7.value = aUI[7].value;
vC7.onClick = function() { aUI[7].value = !aUI[7].value; } // boolean not function
var buttons = myWindow.add ("group")
buttons.add ("button", undefined, "OK");
// show dialog
myWindow.show();
JO = fUItoJSON(aUI);
// show the result in JSON
alert(fJsonString(JO));
// end of javascript
You can change the there fields in the UI and it will be part of the JSON, when the dialog is closed.
Only the the checkbox I need a extra variable. But that's much better as to create over 50 for every JSON entry.
Have a good time and good night from Germany.
– j.
Copy link to clipboard
Copied
Here is a small update for update the UI as well:
function fJSONtoUI(pJSON) { // converts values of a linear JSON to a linear array and updates the UI
var vJString = fJsonString(pJSON);
if (vJString.indexOf(", ") > -1) { vSplit = ", "; } else { vSplit=","; } // comma + space or just comma
vJString = fDeleteFL(vJString,"}");
var aArray = vJString.split(vSplit);
for (x=0; x < aArray.length; x++){
var aRow = aArray[x].split(":");
aJSONkeys.push(aRow[0]);
if (aRow[1].indexOf(vQuote) > -1) { aUI.push({text:null}); aUI[x].text = fDeleteFL(aRow[1],vQuote); }
else { aUI.push({value:null}); aUI[x].value = aRow[1]; }
}
return aUI;
}
Becaue the dialog need commands like
aUI[x].text = fDeleteFL(aRow[1],vQuote);
without that there is no refresh in the fields of the UI.
Copy link to clipboard
Copied
Hallo Jens,
mit JSON kann ich dir leider nicht weiterhelfen.
Deshalb nur eine kleine Randnotiz
Es gibt sicherlich Dutzende unterschiedlicher Wege, um ans Ziel zu kommen. Könnte es sein, dass z.B. deine fJsonString () function durch eine etwas abgewandelte Schreibweise direkter ausgeführt wird?
function fJsonString(pObject) { // ObjectToString
//alert( pObject.toSource().toString().replace (/[()]/g, ""));
return pObject.toSource().toString().replace (/[()]/g, "");
}
Copy link to clipboard
Copied
Hallo Pixxelschubser
Du hast recht, mit regexp geht's auch bzw. besser. Ich bin einfach zu sehr noch in der klassischen Programmierung verhaftet, dass ich eher über javascript-string-befehle (nach)denke und gehe. 🙂
JSON ist eine sehr lohnende Sache, wenn es um Voreinstellungen und Datenaustausch geht. Komplexe Strukturen habe ich damit bisher nicht gebaut. Jedoch meine über 50 Eingabefehler in einer Benutzeroberfläche, die sechs Karteireiter umfasst, kann ich einfach als Textdatei (Endung .json) schreiben und lesen und damit die Benutzeroberfläche auffüllen, so dass der Anwender ggf nur noch Änderungen eintragen kann, wie z. B. den Projektnamen.
Bis auf die Checkboxen kann ich zwischen JSON und struktiertem Array einfach hin- und herwechseln. Feine Sache.
Schönen Abend
– j.
Nachtrag: Hier noch ein Ausschnitt aus der Benutzeroberfläche. Nach dem Laden der JSON-Datei wechselt der Name für das Projekt und viele, viele andere Felder auch.
Copy link to clipboard
Copied
Hallo Jens,
Bitte verstehe mich nicht falsch. Aber die ganze Herangehensweise ist doch nur eine „Krücke“. Vielleicht verstehe ich auch einfach dein Gesamtkonzept nicht wirklich und ich weiß nicht, was du wirklich benötigst.
Es stimmt, Objekte - nennen wir sie einmal mehrstufige Arrays - sind mit Javascript nicht besonders gut zu händeln. Allerdings kann man sie meiner Meinung nach doch recht simpel „auflösen“ und so mit den Elementen des Objektes ganz gut umgehen.
Ich glaube nicht, dass es wirklich notwendig ist, alles umständlich in einen String umzuwandeln. Dann mühseliges Hin- und Herspringen zwischen Funktionen mit diversen verschachtelten IndexOf- und If-Else Ersetzen-Vorgängen, For-Schleifen und anschließendem mehrfaches Rückumwandeln in multiple Arrays …
Mein Vorschlag:
Lies die Werte direkt aus - oder speichere die keys and values in einem Array zwischen. Versuche z.B. einmal den nachfolgenden Weg:
var JO = { JSname: "Plus",JSversion: "1.0",uiProject: "Mustermann", uiPrintFormat: "ai", uiCuttingFormat: "pdf", uiPrintSaveFolder: "~/Desktop/", uiPrintSave: true, uiCuttingSave: true, uiPageSize: "A4", uiGridSheetWidth: 210.0, uiGridSheetHeight: 297.0, uiPageMarginLeft: 7.0, uiPageMarginRight: 7.0, uiPageMarginTop: 7.0, uiPageMarginBottom: 7.0, uiShowPageMargin: true, }; // the JSON object
var aUI = new Array (); // the values for the UI
var aJSONkeys = new Array (); // the JSON keys
var key;
// entweder direktes Auslesen
alert("value 1 direkt: " + JO.JSname);
alert("value 4 direkt: " + JO.uiPrintFormat);
// oder zwischenspeichern in "deinen" Arrays
fJSONtoUI(JO);
alert("length of aUI array: " + aUI.length + "\nfirst key: " + aJSONkeys[0] + "\nfirst value: " + aUI[0]);
alert("length of aJSONkeys array: " + aJSONkeys.length + "\nfourth key: " + aJSONkeys[3] + "\nfourth value: " + aUI[3]);
function fJSONtoUI(pJSON) {
for (key in pJSON) {
aJSONkeys.push(key);
aUI.push(pJSON[key]);
}
return aUI, aJSONkeys;
}
oder als Variante „dein Weg“
(über String-Konverierung-->Mehrfach-Ersetzen-->Array-Zwischenkonvertierung-->FinaleArrays) nur etwas aufgeräumt und optimiert:
var JO = { JSname: "Plus",JSversion: "1.0",uiProject: "Mustermann", uiPrintFormat: "ai", uiCuttingFormat: "pdf", uiPrintSaveFolder: "~/Desktop/", uiPrintSave: true, uiCuttingSave: true, uiPageSize: "A4", uiGridSheetWidth: 210.0, uiGridSheetHeight: 297.0, uiPageMarginLeft: 7.0, uiPageMarginRight: 7.0, uiPageMarginTop: 7.0, uiPageMarginBottom: 7.0, uiShowPageMargin: true, }; // the JSON object
var aUI = new Array (); // the values for the UI
var aJSONkeys = new Array (); // the JSON keys
var aArray = JO.toSource().toString().replace (/[(){}\"]/g, "").split(/, ?/);
for (x=0; x < aArray.length; x++) {
var aRow = aArray[x].split(":");
aJSONkeys.push(aRow[0]);
aUI.push(aRow[1]);
}
alert("length of array: " + aUI.length + "\nfirst value: " + aUI[0]);
alert("aUI:\n" + aUI);
alert("aJSONkeys:\n" + aJSONkeys);
Copy link to clipboard
Copied
I think OP is not informed of the JSON.parse method. This method works like so: var obj = JSON.parse(inStr);
You can read in the text with a file-read, then use JSON.parse on the text to turn it into a JavaScript object instantly.
It has advantages of security and being able to store arbitrary structures with different data types which will preserve their type - such as boolean for checkbox.
An advantage of being able to use a structure is that your object does not have to be just linear anymore such as { "prop_1" : "ABC", "prop_2" : 123 }, it can be more complex and allow you to do more things:
{
"prop_1" : { "label" : "My Prop #1", "value" : "ABC" },
"prop_2" : { "label" : "My Prop #2", "value" : 123 },
},
^^ With this you can make the display label different from the code-name, .. you can use keys which are not display-names, etc.
That code-cloud of the minified JSON object is all the code needed to transform strings back and forth.
Check this code out and you can see the window it can make using this exact code here:
#target illustrator
function test(){
"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function quote(t){
return escapable.lastIndex=0,escapable.test(t)?'"'+t.replace(escapable,function(t){var e=meta[t];
return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}
function str(t,e){var n,r,o,f,u,i=gap,p=e[t];switch(p&&"object"==typeof p&&"function"==typeof p.toJSON&&(p=p.toJSON(t)),
"function"==typeof rep&&(p=rep.call(e,t,p)),typeof p){case"string":return quote(p);case"number":return isFinite(p)?String(p):"null";
case"boolean":case"null":return String(p);case"object":if(!p)return"null";if(gap+=indent,u=[],"[object Array]"===Object.prototype.toString.apply(p)){
for(f=p.length,n=0;f>n;n+=1)u[n]=str(n,p)||"null";return o=0===u.length?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+i+"]":"["+u.join(",")+"]",gap=i,o}
if(rep&&"object"==typeof rep)for(f=rep.length,n=0;f>n;n+=1)"string"==typeof rep[n]&&(r=rep[n],o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));
else for(r in p)Object.prototype.hasOwnProperty.call(p,r)&&(o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));return o=0===u.length?"{}":gap?"{\n"+gap+
u.join(",\n"+gap)+"\n"+i+"}":"{"+u.join(",")+"}",gap=i,o}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){
return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+
f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){
return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&
(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,n){var r;
if(gap="",indent="","number"==typeof n)for(r=0;n>r;r+=1)indent+=" ";else"string"==typeof n&&(indent=n);if(rep=e,
e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),
"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
JSON.parse=function(text,reviver){function walk(t,e){var n,r,o=t[e];if(o&&"object"==typeof o)for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&
(r=walk(o,n),void 0!==r?o[n]=r:delete o[n]);return reviver.call(t,e,o)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&
(text=text.replace(cx,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),
/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]")
.replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;
throw new SyntaxError("JSON.parse")})}();
// Triple-quotes, a special ExtendScript-only feature.
var inStr = """{
"prop_1" : { "label" : "My Prop #1", "value" : "ABC" },
"prop_2" : { "label" : "My Prop #2", "value" : 123 },
"prop_3" : { "label" : "My Prop #3", "value" : true },
}""";
var obj = JSON.parse(inStr);
alert(obj.prop_1.value); // "ABC"
var objAsJsonString = JSON.stringify(obj, null, 2);
alert(objAsJsonString); // <-- WRITE this to a file.
var w = new Window("dialog");
var newGroup, newLabel, newInput, thisItem;
for (var all in obj) {
thisItem = obj[all];
newGroup = w.add("group");
if (typeof thisItem.value == "boolean") { // Because of JSON object, we can correctly assign a checkbox to something we sense is a boolean data type.
newInput = newGroup.add("checkbox", undefined, thisItem.label);
newInput.value = thisItem.value;
} else {
// Assign labels to things other than checkboxes because they have a built-in label feature.
newLabel = newGroup.add("statictext", undefined, thisItem.label);
newInput = newGroup.add("edittext", undefined, thisItem.value);
}
}
var btn_ok = w.add("button", undefined, "Ok");
w.show();
};
test();
Copy link to clipboard
Copied
Here is one which writes and reads the JSON from your desktop.
#target illustrator
function test(){
"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function quote(t){
return escapable.lastIndex=0,escapable.test(t)?'"'+t.replace(escapable,function(t){var e=meta[t];
return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}
function str(t,e){var n,r,o,f,u,i=gap,p=e[t];switch(p&&"object"==typeof p&&"function"==typeof p.toJSON&&(p=p.toJSON(t)),
"function"==typeof rep&&(p=rep.call(e,t,p)),typeof p){case"string":return quote(p);case"number":return isFinite(p)?String(p):"null";
case"boolean":case"null":return String(p);case"object":if(!p)return"null";if(gap+=indent,u=[],"[object Array]"===Object.prototype.toString.apply(p)){
for(f=p.length,n=0;f>n;n+=1)u[n]=str(n,p)||"null";return o=0===u.length?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+i+"]":"["+u.join(",")+"]",gap=i,o}
if(rep&&"object"==typeof rep)for(f=rep.length,n=0;f>n;n+=1)"string"==typeof rep[n]&&(r=rep[n],o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));
else for(r in p)Object.prototype.hasOwnProperty.call(p,r)&&(o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));return o=0===u.length?"{}":gap?"{\n"+gap+
u.join(",\n"+gap)+"\n"+i+"}":"{"+u.join(",")+"}",gap=i,o}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){
return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+
f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){
return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&
(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,n){var r;
if(gap="",indent="","number"==typeof n)for(r=0;n>r;r+=1)indent+=" ";else"string"==typeof n&&(indent=n);if(rep=e,
e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),
"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
JSON.parse=function(text,reviver){function walk(t,e){var n,r,o=t[e];if(o&&"object"==typeof o)for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&
(r=walk(o,n),void 0!==r?o[n]=r:delete o[n]);return reviver.call(t,e,o)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&
(text=text.replace(cx,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),
/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]")
.replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;
throw new SyntaxError("JSON.parse")})}();
// Triple-quotes, a special ExtendScript-only feature.
// Default object.
var inStr = """{
"prop_1" : { "label" : "My Prop #1", "value" : "ABC" },
"prop_2" : { "label" : "My Prop #2", "value" : 123 },
"prop_3" : { "label" : "My Prop #3", "value" : true },
}""";
var savedSettingsFile = File("~/Desktop/mySettings.json"); // You can also save them as .txt which can help on Windows due to not being able to preview JSON in File Explorer
if (!savedSettingsFile.exists) {
savedSettingsFile.open("w");
savedSettingsFile.write(inStr);
savedSettingsFile.close();
} else {
savedSettingsFile.open("r");
inStr = savedSettingsFile.read();
savedSettingsFile.close();
}
var obj = JSON.parse(inStr);
var w = new Window("dialog");
var newGroup, newLabel, newInput, thisItem;
var uiElements = {};
for (var all in obj) {
thisItem = obj[all];
newGroup = w.add("group");
if (typeof thisItem.value == "boolean") { // Because of JSON object, we can correctly assign a checkbox to something we sense is a boolean data type.
newInput = newGroup.add("checkbox", undefined, thisItem.label);
newInput.value = thisItem.value;
} else {
// Assign labels to things other than checkboxes because they have a built-in label feature.
newLabel = newGroup.add("statictext", undefined, thisItem.label);
newInput = newGroup.add("edittext", undefined, thisItem.value);
}
uiElements[all] = newInput;
}
var btn_ok = w.add("button", undefined, "Ok");
if (w.show() == 2) {
return null;
} else {
var objFromUi = {
prop_1 : { label : obj["prop_1"].label, value : uiElements["prop_1"].text },
prop_2 : { label : obj["prop_2"].label, value : uiElements["prop_2"].text },
prop_3 : { label : obj["prop_3"].label, value : uiElements["prop_3"].value },
};
savedSettingsFile.open("w");
savedSettingsFile.write(JSON.stringify(objFromUi, null, 2));
savedSettingsFile.close();
}
};
test();
Copy link to clipboard
Copied
The objFromUi is for demonstration, in reality you would just mutate the 'obj', the original object with the new values:
for (var all in uiElements) {
obj[all].value = (uiElements[all] instanceof Checkbox)? uiElements[all].value : uiElements[all].text; // Mutate the value
}
// stuff for writing the file...
file.write(JSON.stringify(obj));
Copy link to clipboard
Copied
"I think OP is not informed of the JSON.parse method. This method works like so: var obj = JSON.parse(inStr);"
I have read up on the topic of JSON. Unfortunately, you can't use JSON.parse in AI without a library. See screenshot. In addition, this library is very large and why should I go to all this trouble for 10 or 50 entries of my simple JSON when I can do the conversion successfully with two small functions.
My code may not be elegant, but it serves the purpose I wanted: Read JSON file, update all fields of the user interface, write the fields as JSON file to disk.
Copy link to clipboard
Copied
Guten Morgen und Danke für Deinen Code.
Schön, wenn es so einfach wäre. Jedoch brauche ich für die Eingabefelder Variablen wie vInput, die entweder vInput.text für Texteingaben oder vInput.value für Zahlen und Boolean. Zum Aktualisieren der Eingabefelder nach dem Öffnen/Lesen der JSON Datei muss die Zuweisung mit Gleichheitszeichen erfolgen. vInput.text = "Neuer Text". Das nur Auffüllen des Array mit neuen Werten reicht nicht.
Meine Lösung mag nicht elegant sein, jedoch sie erfüllt ihren Zweck. 🙂
– j.
Copy link to clipboard
Copied
I have provided examples with the large library in them. There are numerous problems which could potentially be very bad issues with a non JSON-object approach, but one issue is that as pixxel implied, is that a home-made function that does anything other than produce the same exact result as the JSON object (by doing all the things to all the things that it does), you only will get valid JSON by accident (so you might as well use .txt extension anyway; it's an arbitrary text format of your engineering at this point). It could be very well that you are going to create conditions to enforce this 'accident' 100% of the time, however it is dangerous.
So, indeed Ai does not have the JSON object, that's why in my screenshot, in my snippets all over this thread there is the huge library pasted right in, its the 'JSON object'. It's sure scary and ugly, but don't worry: just put it into a separate file called JSON.jsx and use #include "path/path/JSON.jsx" in your scripts to take advantage of this object. Try to paste my last large snippet into your ESTK and view the magic results, it is obvious you have not done this yet and you are missing out indeed.