jpath

alexeyten edited this page Jan 20, 2012 · 5 revisions
Clone this wiki locally

jpath

jpath — это язык запросов для доступа к различным элементам javascript-дерева. В общем-то, это аналог xpath, с поправкой на javascript синтаксис. Кроме того, из-за естественных ограничений javascript-объектов, в нем нет некоторых возможностей (например, осей).

javascript-дерево

Для простоты можно считать, что речь идет про json. Т.е. про древовидную структуру, построенную из javascript'овых объектов, массиов и "скаляров". К сожалению (или к счастью), json намного беднее xml, так что приходится идти на некоторые компромиссы.

Однозначного соответствия между xml-документами и js-документами нет. Более-менее, можно для каждого js-объекта построить соотвествующий ему xml-фрагмент (не всегда xml-документ).

Объекты

Вот простейший объект:

{
    id: 1,
    title: "first",
    disabled: true
}

Ему соотвествует вот такой xml-фрагмент:

<id>1</id>
<title>first</title>
<disabled/>

Важно. Это не xml-документ, у него нет корневого элемента. Просто потому, что неизвестно какое имя может быть у этого элемента -- в самом js-объекте никаких следов этого имени нет, но оно может быть определено из контекста, в котором находится этот js-объект:

{
    id: 42,
    data: {
        id: 1,
        title: "first",
        disabled: true
    }
}

Здесь внешний объект по-прежнему не имеет имени, но внутреннему объекту можно назначить имя data:

<id>42</id>
<data>
    <id>1</id>
    <title>first</title>
    <disabled/>
</data>

Т.е., грубо говоря, "полноценный" xml-документ получается из вот такой (бессмысленной в javascript'е) конструкции:

data: {
    id: 1,
    title: "first",
    disabled: true
}

Массивы

Массив:

[ 1, 2, 3 ]

Соответствующий ему xml-фрагмент:

<item>1</item>
<item>2</item>
<item>3</item>

Т.е. каждый элемент массива — это отдельный xml-элемент (сам массив при этом своего собственного элемента не имеет). При этом имя этого элемента либо (как и для объектов) определяется из контекста, либо же назначается равным item для безымянных массивов.

{
    id: 42,
    data: [ 1, 2, 3 ]
}

<id>42</id>
<data>1</data>
<data>2</data>
<data>3</data>

Более развесистый пример:

{
    data: [
        {
            id: 1,
            title: "first",
            disabled: true
        },
        {
            id: 2,
            title: "second"
        },
        {
            id: 3,
            title: "third"
        }
    ]
}

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

<data>
    <id>1</id>
    <title>first</title>
    <disabled/>
</data>
<data>
    <id>2</id>
    <title>second</title>
</data>
<data>
    <id>3</id>
    <title>third</title>
</data>

"Скаляры"

В обычном json помимо объектов и массивов могут встречаться строки, числа, true, false и null. В нашем js-дереве false и null не используются (непонятно, чему их сопоставлять):

{
    id: "2345673457568956867",
    count: 42,
    title: "Hello, World",
    disabled: true
}

<id>2345673457568956867</id>
<count>42</count>
<title>Hello, World</title>
<disabled/>

Важно. Не стоит забывать, что в javascript'е нет целых 64-битных чисел, поэтому иногда лучше использовать строки. Например, для длинных цифровых id-шников.

Синтаксис jpath

В отличие от xpath, где бывают абсолютные и относительные пути, все jpath всегда относительные.

Простые примеры и xpath-аналоги:

.foo            foo
.foo.bar        foo/bar
.               .
..              ..
...             ../..
..foo           ../foo
..foo..bar      ../foo/../bar
.foo.*          foo/*
..*             ../*
.*.*            */*

Все jpath всегда начинаются с символа .

Предикаты

Как и в xpath, в jpath существуют предикаты, позволяющие уточнить выборку:

.items.item [ .disable ]                        items/item[ disabled ]
.items.item[ .count > 0 && .count < 5 ]         items/item[ count &gt; 0 and count &lt; 5 ]

Соответствие jpath и xpath операторов:

a | b           a | b
a + b           a + b       // Числовое сложение только!
a - b           a - b
a * b           a * b
a / b           a div b
a % b           a mod b
a || b          a or b
a && b          a and b
!a              not(a)
a > b           a &gt; b
a >= b          a &gt;= b
a < b           a &lt; b
a <= b          a &lt;= b
a == b          a = b
a != b          a != b

Индексы

В xpath если выражение внутри [...] является числом, то это не предикат, а индекс.

items/item[42]      // выбрать 42-й item.

В jpath разница между предикатом и индексом определяется немного не так. Вот для сравнения два jpath:

.items.item[ .index ]

и

index = 42
.items.item[ index ]

Разница между .index и index в том, что index является "абсолютным" выражением в данном контексте. Т.е. оно не зависит от конкретного item'а. Поэтому это индекс. .index зависит от item'а — это предикат (т.е. мы фильтруем все item'а по условию внутри [...]).