Как сгруппировать XML элементов в дизайне, добавив их в родительский тег - PullRequest
1 голос
/ 06 февраля 2020

У меня есть XML элементы, связанные с элементом root документа, и есть некоторые элементы, которые следует сгруппировать и затем связать с документом проекта, поэтому я хотел бы знать, как я могу создать виртуальную группу и добавить элементы в родительский тег, который, в свою очередь, будет дочерним по отношению к родительскому элементу в сценариях InDesign

Существующий:

-EL1
-EL2
-EL3
-EL4
-EL5

Ожидаемый:

-EL1
-EL
--EL2
--EL3
--EL4
-EL5

Где EL - родитель, а EL2,EL3,EL4 - дочерние элементы.

1 Ответ

2 голосов
/ 10 февраля 2020

Настройка демонстрации .indd

Чтобы обеспечить некоторый контекст для этого ответа, а также для облегчения объяснения и демонстрации, сначала импортируйте следующий документ XML в новый документ inDesign:

sample. xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
  <EL1>Para 1</EL1>
  <EL2>Para 2</EL2>

  <EL3>Para 3</EL3>

  <EL4>Para 4</EL4>

  <EL5>Para 5</EL5>
  <EL6>Para 6</EL6>
  <EL7>Para 7</EL7>
  <EL8>Para 8</EL8>
  <EL9>Para 9</EL9>
</Root>

Чтобы импортировать sample.xml в новый документ, выполните следующие действия:

  1. Создайте новый документ InDesign.
  2. Выберите View> Structure> Show Structure в строке меню, чтобы открыть панель XML Структура .
  3. В структуре XML На панели выберите элемент по умолчанию Root XML.
  4. На панели XML Структура выберите Import XMl в раскрывающемся меню.
  5. Найти вышеупомянутый sample.xml файл и откройте его.
  6. Наконец сохраните документ

Примечание: В этом ответе нам потребуется отменить .indd верните файл в исходное состояние, поэтому сохраните его.

Дерево документов XML теперь должно иметь структуру следует:

Начальная XML древовидная структура:

Root
├── EL1
├── EL2
├── EL3
├── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9

Как вы можете видеть, это очень похоже на то, что вы описали в своем вопросе, однако есть только несколько больше элементов, а именно; EL6, EL7, EL8, EL9.


Решение A

example-a.jsx

#target indesign

// 1. Obtain a reference to the active document.
var doc = app.activeDocument;

// 2. Obtain a reference to the root element 
var root = doc.xmlElements.item(0);

// 3. Create a new tag
var newParentTag = doc.xmlTags.add("EL");

// 4. Create a new element node
var parentNode = root.xmlElements.add(newParentTag.name);

// 5. Change the position of the newly created element node
parentNode.move(LocationOptions.before, root.xmlElements.item(1));

// 6. Move elements to be children of the newly created parent element.
root.xmlElements.item(4).move(LocationOptions.atBeginning, root.xmlElements.item(1));
root.xmlElements.item(3).move(LocationOptions.atBeginning, root.xmlElements.item(1));
root.xmlElements.item(2).move(LocationOptions.atBeginning, root.xmlElements.item(1));

Если мы запустим код, предоставленный в example-a.jsx (выше), он будет реструктурировать дерево XML следующим образом:

XML древовидная структура после:

Root
├── EL1
├── EL
│   ├── EL2
│   ├── EL3
│   └── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9

Примечание: Прежде чем перейти к Решение B (ниже), пожалуйста, верните демонстрационный документ inDesign в исходное состояние. Выберите File> Revert в строке меню.


Решение B

Если вы намереваетесь часто реструктурировать дерево XML, т.е. вы собираетесь двигаться различные дочерние элементы к новому родительскому элементу часто, тогда я рассмотрел бы использование вспомогательной функции, такой как функция childElementsToNewParent, показанная ниже. Делая это, вы можете предоставить более простой интерфейс для выполнения этой задачи.

example-b.jsx

#target indesign

$.level=0;

//------------------------------------------------------------------------------
// Usage
//------------------------------------------------------------------------------

// 1. Obtain a reference to the active document.
var doc = app.activeDocument;


// 2. Obtain a reference to the root element
var rootElement = doc.xmlElements.item(0);


// 3. Restructure the XML tree.
childElementsToNewParent(doc, rootElement, 2, 4);


//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Moves child element(s) at a given position within a given parent element to
 * a new parent element.
 *
 * @param {Object} doc - A document reference for changing its XML structure.
 * @param {Object} parent - The parent XMlElement whose children need to move.
 * @param {Number} from - The position of the first child element to move.
 * @param {Number} to - The position of the last child element to move.
 * @param {Object} options - The configuration options.
 * @param {String} [options.tagName=undefined] - A custom name for the newly
 *  created parent XML element.
 */
function childElementsToNewParent(doc, parent, from, to, options) {

  // Default options
  var opts = {
    tagName: undefined
  }

  // Override the default opts with any user defined options.
  opts = assign(options, opts);

  var xPath = '*[position() >= ' + from + ' and position() <= ' + to + ']';

  var childrenToMove = parent.evaluateXPathExpression(xPath);

  // XMLElements matched by the `evaluateXPathExpression` method are returned
  // in any order. We sort each element object in the array by it positional
  // index to ensure that when we move them to a new parent element their
  // positional order is preserved as-is.
  childrenToMove = sortArrayOfObjects(childrenToMove, 'index');

  var firstChildToMove = childrenToMove[0];
  var firstChildToMoveIndex = firstChildToMove.index;

  var xmlTagName = opts.tagName
      ? opts.tagName
      : firstChildToMove.markupTag.name.substring(0, 2);

  createXmlTag(doc, xmlTagName);

  // Move the newly created parent XMLElement to
  // before the first child element to be moved.
  parent.xmlElements.add(xmlTagName).move(
    LocationOptions.before,
    parent.xmlElements.item(firstChildToMoveIndex)
  );

  // Move each the matched child XMLElement(s) into their new parent XMLElement.
  for (var i = 0, max = childrenToMove.length; i < max; i++) {
    childrenToMove[i].move(
      LocationOptions.atEnd,
      parent.xmlElements.item(firstChildToMoveIndex)
    );
  }
}

/**
 * Enumerates own properties of a 'source' object and copies them to a 'target'
 * object. Properties in the 'target' object are overwritten by properties in
 * the 'source' object if they have the same key.
 *
 * @param {Object} source - The object containing the properties to apply.
 * @param {Object} target - The object to apply the source object properties to.
 * @returns {Object} - The target object.
 */
function assign(source, target) {
  if (typeof source === 'object') {
    for (key in source) {
      if (source.hasOwnProperty(key) && target.hasOwnProperty(key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
}

/**
 * Sorts array of objects by value of property name in ascending order.
 *
 * @param {Array} arr - The array of objects to sort.
 * @param {String} prop - The name of the object property to sort by.
 * @returns {Array} - Array of objects sorted by value of property name.
 */
function sortArrayOfObjects(arr, prop) {
  return arr.sort(function sortByPropertyValue(a, b) {
    if (a[prop] < b[prop]) {
      return -1;
    }
    if (a[prop] > b[prop]) {
      return 1;
    }
    return 0;
  });
}

/**
 * Creates a new XML tag if the given name does not already exist.
 *
 * @param {String} tagName - The name of the XML tag.
 * @param {Object} doc - A reference to the document to add the XMl tag to.
 */
function createXmlTag(doc, tagName) {
  var hasTag = inArray(tagName, doc.xmlTags.everyItem().name);
  if (! hasTag) {
    doc.xmlTags.add(tagName);
  }
}


/**
 * Determines whether an array includes a certain value among its elements.
 *
 * @param {String} valueToFind - The value to search for.
 * @param {Array} arrayToSearch - The array to search in.
 * @returns {Boolean} true if valueToFind is found within the array.
 */
function inArray(valueToFind, arrayToSearch) {
  for (var i = 0, max = arrayToSearch.length; i < max; i++) {
    if (arrayToSearch[i] === valueToFind) {
      return true;
    }
  }
  return false;
}

Если мы запустим код, указанный в example-a.jsx (выше) оно реструктурирует дерево XML до той же результирующей структуры, как показано в древовидной структуре "XML после раздела" "Solution A" .

Примечание: Пока не возвращайте документ indd - оставьте его как есть, чтобы следующий пример использования был актуальным.

Другой пример использования example-b.jsx

Допустим, теперь мы хотим реструктурировать дерево из показанной ранее "XML древовидной структуры после состояния" в следующее состояние:

Следующая желаемая XML древовидная структура:

Root
├── EL1
├── EL
│   ├── EL2
│   └── section
│       ├── EL3
│       └── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9

Для достижения этой структуры нам необходимо заменить следующую строку:

// 3. Restructure the XML tree.
childElementsToNewParent(doc, rootElement, 2, 4);

... которая в настоящее время определена в example-b.jsx, то есть в строке, которая вызывает функцию childElementsToNewParent, вместо нее используются следующие две строки:

var parentElement = rootElement.xmlElements.item(1);
childElementsToNewParent(doc, parentElement, 2, 3, { tagName: 'section' });

На этот раз мы по существу:

  1. Получаем ссылку на элемент EL (так как это родительский элемент, содержащий дочерние элементы, которые мы хотим переместить) и присваиваем его переменная с именем parentElement.

  2. вызов функции childElementsToNewParent со следующими аргументами:

    • doc - ссылка на документ, который мы хотим изменить его XML структуру.
    • parentElement - переменная, значение которой является ссылкой на элемент EL.
    • 2 - первая позиция дочернего элемента, которая мы хотим переместить.
    • 3 - последняя позиция дочернего элемента, который мы хотим переместить.
    • { tagName: 'section' } - объект options, который включает ключ / свойство с именем tagName со значением section.

Примечание: На этот раз мы передали необязательный объект options, в котором указано имя для недавно созданный родительский элемент, а именно section. При вызове функции childElementsToNewParent без предоставления значения для tagName в объекте options мы выводим имя для нового родительского элемента, используя первые два символа первого дочернего элемента, который необходимо переместить. Согласно вашему комментарию:

... имя родителя - это первые два символа выбранных элементов

Дополнительные примечания

Вы заметили, что в последнем примере мы получили ссылку на элемент EL (т. Е. Ссылку на родительский элемент, содержащий дочерние элементы, которые мы хотели переместить), используя следующую запись:

var parentElement = rootElement.xmlElements.item(1);

... который может получить довольно долго при желании получить ссылку на глубоко вложенный элемент. В конечном итоге вы сделаете что-то вроде этого:

var parentElement = rootElement.xmlElements.item(1).xmlElements.item(3).xmlElements.item(1) ....

Я предпочитаю вместо этого использовать метод evaluateXPathExpression, так как это позволяет нам сопоставить элемент с помощью выражение. Например, чтобы получить ссылку на элемент EL, мы могли бы сделать это вместо

var parentElement = rootElement.evaluateXPathExpression('EL')[0];

Как вы можете видеть, мы также используем метод evaluateXPathExpression в теле функции childElementsToNewParent чтобы получить ссылку на дочерние элементы, которые мы хотим переместить:

var xPath = '*[position() >= ' + from + ' and position() <= ' + to + ']';

var childrenToMove = parent.evaluateXPathExpression(xPath);

Это выражение использует функцию XPath position() для поиска дочернего элемента элементы в заданном позиционном диапазоне. По сути, это позиционный диапазон, который вы определяете при передаче аргументов from и to в функцию childElementsToNewParent.

...