在 Extendscript Indesign 中重新排序 XML 个元素

Reorder XML Elements in Extendscript Indesign

我想重新排列以下电影列表,使 version==3D 的电影应该放在 version==2D 的电影之前。

输入

<films>
  <film name="Foobar" version="2D"></film>
  <film name="Foobar" version="3D"></film>
  <film name="Foobaz" version="2D"></film>
  <film name="Foobaz" version="3D"></film>
</films>

期望输出

<films>
  <film name="Foobar" version="3D"></film>
  <film name="Foobar" version="2D"></film>
  <film name="Foobaz" version="3D"></film>
  <film name="Foobaz" version="2D"></film>
</films>

我四处摸索,最终得到了以下代码。希望它是可以理解的。

/***
 Extend Array prototype to have a indeOf function
 ***/
Array.prototype.indexOf = function(item) { 
    var index = 0, length = this.length;  
    for ( ; index < length; index++ ) {
        if ( this[index] == item )  
            return index;  
        }  
    return -1;  
};  


var xmlString = '\
<films>\
    <film name="Foobar" version="2D"></film>\
    <film name="Foobar" version="3D"></film>\
    <film name="Foobaz" version="2D"></film>\
    <film name="Foobaz" version="3D"></film>\
</films>';

var xml = new XML(xmlString);
var sortVersion = ['2D', '3D'];
var uniqueMovies = new Array();


for (var index = 0; index < xml.elements().length(); index++) {
    if (index == 0) continue;

    // Fetch meta data of previous movie
    var prevTitle = xml.elements()[index - 1].@name;
    var prevVersion = xml.elements()[index - 1].@version;
    var prevVersionIdx = sortVersion.indexOf(prevVersion);

    // Fetch meta data of current movie
    var curTitle = xml.elements()[index].@name;
    var curVersion = xml.elements()[index].@version;
    var curVersionIdx = sortVersion.indexOf(curVersion);

    // If both movie title matches verify which movie should be prioritized
    if (prevTitle == curTitle) {
      if (prevVersionIdx < curVersionIdx) { 
          // Movie prio movie before less prio movie
          xml.insertChildBefore(xml.elements()[index - 1], xml.elements()[index]);
          // And delete the next in index
          delete xml.elements()[index + 1];
      }
    }   
}

$.writeln("-----");
$.writeln("");
$.writeln(xml.elements().toString());
return xml

然而,当我 运行 这个脚本时,我以以下结果结束,其中没有任何改变,尽管两个 if 条件都满足并且 index[1] 上的元素添加在 [=18= 之前].

<films>
  <film name="Foobar" version="2D"/>
  <film name="Foobar" version="3D"/>
  <film name="Foobaz" version="2D"/>
  <film name="Foobaz" version="3D"/>
</films>

有人知道我做错了什么吗?

请考虑以下方法:

script.jsx

/**
 * Extend Array prototype to have a indexOf function
 */
Array.prototype.indexOf = function(item) {
    var index = 0, length = this.length;
    for ( ; index < length; index++ ) {
        if ( this[index] == item )
            return index;
        }
    return -1;
};

var xmlString = '\
<films>\
  <film name="Foobar" version="2D"></film>\
  <film name="Foobar" version="3D"></film>\
  <film name="Foobaz" version="2D"></film>\
  <film name="Foobaz" version="3D"></film>\
  <film name="Foo" version="3D"></film>\
  <film name="Quux" version="2D"></film>\
  <film name="Quux" version="3D"></film>\
</films>';

var xml = new XML(xmlString);
var sortVersion = ['2D', '3D'];

// 1. Create a temporary XML object with a  matching `films` root element.
var tempXml = new XML('<' + xml.name() + '></' + xml.name() + '>');

for (var index = 0, max = xml.elements().length(); index < max; index++) {

  // Fetch meta data of previous movie
  var prevTitle = String(xml.elements()[index - 1].@name);
  var prevVersion = xml.elements()[index - 1].@version;
  var prevVersionIdx = sortVersion.indexOf(prevVersion);

  // Fetch meta data of current movie
  var curTitle = String(xml.elements()[index].@name);
  var curVersion = xml.elements()[index].@version;
  var curVersionIdx = sortVersion.indexOf(curVersion);

  // 2. Insert each XML element node from the original XML Object into
  // the temporary XML Object and position (i.e. sort) as neccessary.
  if (prevTitle === curTitle && prevVersion < curVersion) {
    tempXml.insertChildBefore(tempXml.elements()[index -1], xml.elements()[index])
  } else {
    // There's nothing to change (i.e. sort) so insert it as-is .
    tempXml.insertChildBefore(tempXml.elements()[index], xml.elements()[index])
  }
}

// 3. Overwrite original XML object's children with temporary (sorted) XML objects children.
xml.setChildren(tempXml.children());

// 4. Delete temporary XML object.
tempXml = undefined;


// Testing...
$.writeln(xml)
$.writeln('-----------------')
$.writeln(xml.elements().length())
$.writeln('-----------------')

解释:

上面的要点不是试图操纵(即排序)原始 XML 对象,而是执行以下操作:

  1. 创建另一个仅包含 <films> 根元素的 XML 对象。此 XML 对象将是临时的。

  2. for 循环的每一轮中,我们;

    • 将每个 XML 元素节点的克隆从原始 XML 对象插入临时 XML 对象。
    • 根据需要定位(即排序)每个 XML 元素节点。
  3. 用temporary/sortedXML对象的子元素节点覆盖原来的XML对象子元素节点。

  4. 最后我们删除了临时 XML 对象(即将其设置为 undefined),因为它不再需要了。


结果

上面的示例要点包括以下来源 XML:

<films>
  <film name="Foobar" version="2D"></film>
  <film name="Foobar" version="3D"></film>
  <film name="Foobaz" version="2D"></film>
  <film name="Foobaz" version="3D"></film>
  <film name="Foo" version="3D"></film>
  <film name="Quux" version="2D"></film>
  <film name="Quux" version="3D"></film>
</films>

将转换为以下内容:

<films>
  <film name="Foobar" version="3D"/>
  <film name="Foobar" version="2D"/>
  <film name="Foobaz" version="3D"/>
  <film name="Foobaz" version="2D"/>
  <film name="Foo" version="3D"/>
  <film name="Quux" version="3D"/>
  <film name="Quux" version="2D"/>
</films>

注意: 对于比您问题中提供的示例更复杂的 XML 结构和转换要求,我会考虑利用 XSLT instead. Given your current requirement a template like this one 来实现您的期望的结果。