Skip to main content
Parts4Arts
Inspiring
February 10, 2022
Answered

the best way to bring JSON and UI together

  • February 10, 2022
  • 3 replies
  • 3935 views

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.

This topic has been closed for replies.
Correct answer Parts4Arts

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;
}


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.

3 replies

pixxxelschubser
Community Expert
Community Expert
February 11, 2022

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);

 

 

Silly-V
Legend
February 11, 2022

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();
Silly-V
Legend
February 11, 2022

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();
pixxxelschubser
Community Expert
Community Expert
February 10, 2022

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, "");
}

 

 

Parts4Arts
Inspiring
February 10, 2022

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.

Silly-V
Legend
February 10, 2022

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;

  switch (key) {
    case "string":
        control = parent.add("edittext", undefined, typeDescObj.defaultValue);
      break;
    case "number":
        control = parent.add("slider", undefined, typeDescObj.defaultValue); // The typeDescObj would need extra properties for different controls. Such as 'min/max' on a 'slider'.
      break;
    default:
        throw new Error("There wasn't a control-creation block found for '" + name + "' [" + type + "].");
      break;
  }

  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.

 

Parts4Arts
Inspiring
February 10, 2022

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
}
To tested yet, but perhabs I can use array to let the javascript do the job.
I will try and report about it.
– j.
Silly-V
Legend
February 10, 2022

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.