需要在ExtendScript中将对象转换为JSON字符串

10

我正在使用ExtentdScript处理我的Indesign文档链接的元数据信息。

我想使用JSON.stringify将对象转换为字符串,但是当我使用它时,出现错误,提示:

无法在目标引擎中执行脚本

如果从以下代码中删除linkObjStr = JSON.stringify(linksInfObj);,那么一切都正常工作。

ExtendScript中与JSON.stringify等效的方法是什么,或者是否有其他可能性以正确显示linksInfObj的内容而不是[object object]

for (var i = 0, len = doc.links.length; i < len; i++) {

    var linkFilepath = File(doc.links[i].filePath).fsName;
    var linkFileName = doc.links[i].name;

    var xmpFile = new XMPFile(linkFilepath, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_READ);
    var allXMP = xmpFile.getXMP();

    // Retrieve values from external links XMP.
    var documentID = allXMP.getProperty(XMPConst.NS_XMP_MM, 'DocumentID', XMPConst.STRING);
    var instanceID = allXMP.getProperty(XMPConst.NS_XMP_MM, 'InstanceID', XMPConst.STRING);
    linksInfObj[linkFileName] = {'docId': documentID, 'insId': instanceID};
    linkObjStr = JSON.stringify(linksInfObj);

    alert('Object' + linksInfObj, true); // I am getting [Object Object] here
    alert('String' + linkObjStr, true);

}

如果您正在使用VSCode调试器,根据我的经验,该错误可能是语法错误或未找到包含文件的原因。 - Miguel
2个回答

22

在ExtendScript中,没有包括与解析相关的JSON对象和方法,如JSON.parse()JSON.stringify()。它也没有提供任何其他内置功能来解析JSON。


解决方案:

考虑使用polyfill提供JSON功能,例如Douglas Crockford创建的JSON-js

步骤:

  1. 从Github repo下载名为json2.js的JavaScript文件,并将其保存在与您的.jsx文件相同的位置/文件夹中。

    注意:如果愿意,您可以只复制并粘贴相同Github repo中的json2.js的原始版本来手动创建json2.js文件。

  2. 然后,在当前的.jsx文件顶部,您需要通过添加以下代码行#include引入json2.js文件:

     #include "json2.js";
    

    这类似于您如何在现代JavaScript(ES6)中使用import语句来包含一个模块。

    如果您决定将json2.js文件保存在与您的.jsx文件不同的位置/文件夹中,则可以提供指向该文件位置的路径名

    通过在您的.jsx文件中包括json2.js,您现在将拥有可用的JSON方法:JSON.parse()JSON.stringify()

    以下ExtendScript(.jsx)是一个工作示例,生成JSON以指示与当前InDesign文档(.indd)相关联的所有链接。

    example.jsx

    #include "json2.js";
    
    $.level=0;
    
    var doc = app.activeDocument;
    
    /**
     * Loads the AdobeXMPScript library.
     * @returns {Boolean} True if the library loaded successfully, otherwise false.
     */
    function loadXMPLibrary() {
      if (!ExternalObject.AdobeXMPScript) {
        try {
          ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
        } catch (e) {
          alert('Failed loading AdobeXMPScript library\n' + e.message, 'Error', true);
          return false;
        }
      }
      return true;
    }
    
    /**
     * Obtains the values f XMP properties for `DocumentID` and `instanceID` in
     * each linked file associated with an InDesign document (.indd). A returns the
     * information formatted as JSON,
     * @param {Object} doc - A reference to the .indd to check.
     * @returns {String} - The information formatted as JSON.
     */
    function getLinksInfoAsJson(doc) {
      var linksInfObj = {};
    
      linksInfObj['indd-name'] = doc.name;
      linksInfObj.location = doc.filePath.fsName;
      linksInfObj.links = [];
    
      for (var i = 0, len = doc.links.length; i < len; i++) {
        var linkFilepath = File(doc.links[i].filePath).fsName;
        var linkFileName = doc.links[i].name;
    
        var xmpFile = new XMPFile(linkFilepath, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_READ);
        var allXMP = xmpFile.getXMP();
    
        // Retrieve values from external links XMP.
        var documentID = allXMP.getProperty(XMPConst.NS_XMP_MM, 'DocumentID', XMPConst.STRING);
        var instanceID = allXMP.getProperty(XMPConst.NS_XMP_MM, 'InstanceID', XMPConst.STRING);
    
        // Ensure we produce valid JSON...
        // - When `instanceID` or `documentID` values equal `undefined` change to `null`.
        // - When `instanceID` or `documentID` exist ensure it's a String.
        instanceID = instanceID ? String(instanceID) : null;
        documentID = documentID ? String(documentID) : null;
    
        linksInfObj.links.push({
          'name': linkFileName,
          'path': linkFilepath,
          'docId': documentID,
          'insId': instanceID
        });
      }
    
      return JSON.stringify(linksInfObj, null, 2);
    }
    
    if (loadXMPLibrary()) {
     var linksJson = getLinksInfoAsJson(doc);
     $.writeln(linksJson);
    }
    

    输出:

    运行上述脚本将会在您的控制台上记录JSON格式,类似于以下示例:

    {
      "indd-name": "foobar.indd",
      "location": "/path/to/the/document",
      "links":[
        {
          "name": "one.psd",
          "path": "/path/to/the/document/links/one.psd",
          "docId": "5E3AE91C0E2AD0A57A0318E078A125D6",
          "insId": "xmp.iid:0480117407206811AFFD9EEDCD311C32"
        },
        {
          "name": "two.jpg",
          "path": "/path/to/the/document/links/two.jpg",
          "docId": "EDC4CCF902ED087F654B6AB54C57A833",
          "insId": "xmp.iid:FE7F117407206811A61394AAF02B0DD6"
        },
        {
          "name": "three.png",
          "path": "/path/to/the/document/links/three.png",
          "docId": null,
          "insId": null
        }
      ]
    }
    
    注意事项:对于 JSON 的建模:
    你可能已经注意到,上面的 JSON 输出结构与你在给定示例中尝试构造的结构不同。主要区别在于,你使用链接文件名作为属性/键名,例如以下示例:
    {
      "one.psd": {
        "docId": "5E3AE91C0E2AD0A57A0318E078A125D6",
        "insId": "xmp.iid:0480117407206811AFFD9EEDCD311C32"
      },
      "two.jpg": {
        "docId": "EDC4CCF902ED087F654B6AB54C57A833",
        "insId": "xmp.iid:FE7F117407206811A61394AAF02B0DD6"
      }
      ...
    }
    

    这种生成类似于示例的JSON并不理想,因为如果您有两个链接,它们都具有相同的名称,那么您只会报告其中一个。在对象内部,您不能有两个具有相同名称的属性/键。


    编辑:

    针对OP的评论做出回应:

    嗨,RobC,除了使用#include 'json2.js'外,还有其他方法可以在JSX文件中包含外部js文件吗?

    以下是几种替代方法:

    1. 您可以利用$.evalFile()。例如,将#include"json2.js";替换为以下两行:

       var json2 = File($.fileName).path + "/" + "json2.js";
       $.evalFile(json2);
      

      注意: 这个例子假设json2.js与您的.jsx文件在同一文件夹中。

      或者,如果你想完全避免存在额外的json2.js文件。你可以在你的.jsx文件的顶部添加一个IIFE(立即调用函数表达式),然后将json2.js文件的内容复制粘贴到其中。例如:

       (function () {
           // <-- Paste the content of `json2.js` here.
       })();
      

      注意:如果代码大小是一个问题,那么请考虑在将json2.js的内容粘贴到IIFE之前对其进行压缩


就此而言, json2.js 在 ExtendScript 方面存在一些问题,特别是枚举转换方面。一个人可能更喜欢 Marc Autret 使用 IDExtenso 框架的自己的实现:https://github.com/indiscripts/IdExtenso - Loic
有趣的是,但不幸的是IDExtenso只能在ExtendScript ToolKit(ESTK)中使用,而不能在Visual Studio Code中使用。 - Elise van Looij
JSON-js很好用 - 直到有人试图在Visual Studio Code的ExtendScript CEP面板中使用它。在VSCode的独立.jsx中测试,JSON.stringify()和JSON.parse()可以编码和解码对象和对象数组。但是通过evalScript()发送完全相同的数据,你只会得到“EvalScript错误”,如果你很幸运的话。这真的太糟糕了,但我又回到了join()和split()。 - Elise van Looij
1
此外,在 VSCode 中引用 .jsx 文件的 include 语句为://@include "./JSON-js/json2.js" - Elise van Looij
正如你所想象的那样,EvalScript 可能不起作用,因为任何类型的 include 都依赖于实际文件位置,而 EvalScript 中的脚本没有物理位置。当我使用 evalJSXFile 时也遇到了类似的问题,显然它也无法工作,因为 ExtendScript 不会获取那个被包含的脚本。如果你必须在脚本中使用 JSON,你可以通过将整个 json2.js 嵌入到你的脚本中来解决这个问题。 - toyssamurai

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接