Преобразование документов Atom в формат JSON

Как избежать потери важных данных и контекстной информации в процессе преобразования документов Atom в формат JSON

На первый взгляд может показаться, что в преобразовании документов Atom в формат JSON нет ничего сложного. Ленты Atom – это не что иное, как документы XML, к тому же существует множество утилит для преобразования из XML в JSON. Однако формат Atom – это нечто большее, чем просто набор элементов и атрибутов XML. Существует множество нюансов, затрудняющих работу с Atom. Целью данной статьи является описание этих трудностей, а также механизма, реализованного в проекте Apache Abdera, с помощью которого документы Atom преобразуются в легкочитаемые и законченные фрагменты в формате JSON.

Джеймс Снелл, разработчик программного обеспечения, IBM

Джеймс Снелл (James Snell) является членом исследовательской лаборатории IBM WebAhead, занимающейся разработкой опытных образцов стандартов и технологий программного обеспечения, находящегося в стадии создания, для собственных нужд IBM. Его научно-исследовательские интересы охватывают широкий круг направлений современных технологий, а именно Atom, AJAX, REST, Open Source, персональные издательские системы, семантические и ситуационные Web-приложения. Он является активным участником проекта Apache Abdera, находящегося в настоящее время на этапе разработки и нацеленного на создание высокоэффективной и функционально законченной реализации стандартов формата синдикации Atom и протокола публикации Atom.



09.02.2010

При преобразовании ленты Atom в формат JSON содержимое документа, представленное в гибком и мощном формате XML, переводится в значительно более простую форму. Формат JSON легче использовать в ситуациях, когда полноценный разбор, анализ и обработка документов XML не представляется возможной. Самое главное при преобразовании – это обеспечить гарантии того, что все важные данные и контекстная информация не будут потеряны. В этой статье обсуждаются ключевые моменты, которым необходимо уделить внимание при конвертировании документов Atom. При этом подразумевается, что читатели имеют по крайней мере базовое представление о Atom и JSON. Если вы не знакомы с каким-то из этих стандартов, то обратитесь к материалам, ссылки на которые приведены в разделе Ресурсы, перед прочтением статьи.

Указание языка с помощью метаданных

Информация, содержащаяся в документах Atom, может служить как для человеческого, так и машинного восприятия. Для правильного представления данных, предназначенных для чтения человеком, например заголовков, названий категорий, ссылок, правовых описаний и т.д., в процессе преобразования в JSON необходимо сохранить языковый контекст, присутствующий в Atom.

Указание языка описания данных в Atom осуществляется с помощью атрибута xml:lang. Данный атрибут может использоваться в любом месте в документе Atom. При этом значение атрибута может либо наследоваться, либо переопределяться дочерними элементами. Само значение представляет собой “языковый тег”, определенный в соответствии с документом RFC 4646 под заголовком “Теги идентификации языков”. Эти теги служат для указания языка, письменной системы, региональных диалектов и т.д., на котором написан тот или иной текст. Подобная информация (метаданные) может оказывать влияние на представление данных клиентским программным обеспечением.

В листинге 1 показан пример ленты, которая хоть и выглядит немного искусственно, но, тем не менее, демонстрирует использование атрибута xml:lang в документе Atom.

Листинг 1. Пример ленты Atom, использующей атрибут xml:lang
<feed xmlns="http://www.w3.org/2005/Atom" 
      xml:lang="en-US"> 
  <title>This is the title</title>
  ... 
  <entry xml:lang="fr"> 
    ... 
    <category term="foo" label="bar" xml:lang="en" /> 
    <category term="goo" label="baz" />
    ... 
  </entry> 
</feed>

Атрибут xml:lang элемента <feed> задает языковый контекст по умолчанию для всего документа. Если дочерние элементы, например элемент <title>, явно не переопределяют значение атрибута, то они автоматически наследуют родительский языковый контекст.

Языковый контекст можно сохранить, преобразовав каждый атрибут xml:lang в отдельное поле в формате JSON. Правило преобразования звучит достаточно просто: “добавлять новое языковое поле при каждом изменении языкового контекста”. Пример, на котором показан фрагмент в формате JSON, соответствующий ленте из листинга 1, приведен в листинге 2. Обратите внимание на то, что поле lang присутствует в блоках, соответствующих элементам feed, entry и первому элементу category, но не используется в блоках, описывающих title и второй элемент category, так как последние наследуют родительский языковый контекст.

Листинг 2. Результат преобразования ленты Atom из листинга 1 в формат JSON
{ 
  "lang":"en-US", 
  "title":"This is the title",
  "entries":[ 
    { 
      "lang":"fr" 
      "categories":[ 
        { 
          "lang":"en", 
          "term":"foo", 
          "label":"bar" 
        }, 
        { 
          "term":"goo", 
          "label":"baz" 
        } 
      ] 
    } 
  ] 
}

Атрибут xml:lang может использоваться в любых элементах, чьи атрибуты или дочерние элементы могут содержать данные, для которых важно указание языка. Из базовых элементов Atom таковыми являются feed, entry, title, rights, subtitle, summary, content, category, link, author и contributor.


Идентификаторы IRI и URI

Кроме множества языков в Atom также поддерживаются интернационализированные идентификаторы ресурсов (IRI). Каждый IRI представляет собой URI, в котором допустимо использование символов, не входящих в набор ASCII.

Существуют два варианта использования IRI в документах Atom, и их важно различать при проектировании схемы преобразования в JSON. В примере, приведенном в листинге 3, IRI содержатся в четырех элементах.

Листинг 3. Лента, содержащая несколько интернационализированных идентификаторов ресурсов
<feed xmlns:a="http://www.w3.org/2005/Atom" 
        xml:base="http://examplé.org/foó"> 
  ... 
  <id>http://examplé.org/foó</id>
  <link rel="self" href="" /> 
  <link href="/blog" /> 
  <category scheme="http://examplé.org/foó/bar" term="foo" />
  ... 
</feed>

В элементах id и category IRI используются в качестве идентификаторов. Несмотря на то что они выглядят как обычные URL, они не рассчитаны на использование в запросах. В соответствии со стандартом Atom подобные IRI должны быть абсолютными. При этом они обычно интерпретируются просто как наборы символов, не требующие никакой дополнительной обработки со стороны клиентов Atom. Из элементов Atom только id и category используют IRI в качестве идентификаторов.

В отличие от них элемент link служит специально для указания на ресурсы. Поэтому его атрибут href должен содержать IRI, по которому можно обратиться и получить требуемый ресурс. Значением атрибута может быть относительный путь к ресурсу – в этом случае он должен быть соответствующим образом обработан на клиентской стороне. Примером такой обработки может служить преобразование IRI к эквивалентному URL. Из элементов Atom такие IRI могут содержать элементы icon, logo, uri, link и content.

В листинге 4 показаны элементы id и category, преобразованные в JSON. Отметьте, что IRI были просто скопированы из Atom без какой-либо дополнительной обработки.

Листинг 4. Результат преобразования элементов id и category из листинга 3 в формат JSON
"id":"http://examplé.org/foó" 
"categories":[
  {
    "scheme":"http://examplé.org/foó/bar", 
    "term":"foo"
  }
]

В общем случае, для того, чтобы использовать в JSON IRI, указывающие на ресурсы, они должны быть преобразованы в абсолютные пути и представлены в виде URL, как показано в листинге 5. Обратите внимание на URL "http://xn--exampl-gva.org/fo%C3%B3" – это представленный в ASCII-символах URL, эквивалентный IRI "http://examplé.org/foó".

Листинг 5. Результат преобразования элементов link из листинга 3 в формат JSON
"links":[ 
  { 
    "rel":"self", 
    "href":"http://xn--exampl-gva.org/fo%C3%B3" 
  }, 
  { 
    "href":"http://xn--exampl-gva.org/blog" 
  } 
]

Все подобные IRI и URL, встречающиеся в любом месте документа Atom, должны подвергаться схожей обработке. Это особенно относится к тем из них, которые используются в текстовых значениях, представляющих собой фрагменты HTML или XHTML. Пример показан в листинге 6.

Листинг 6. Фрагмент XHTML, содержащий относительные пути
<a:content type="xhtml"> 
  <div> 
    <img src="/images/foo.jpg" /> 
  </div> 
</a:content>

В JSON не предусмотрено никакого механизма для использования базовых URI, поэтому приложения, работающие с данным форматом, скорее всего не смогут правильно отобразить контент, содержащий относительные URI. Таким образом, важно, чтобы относительные пути автоматически преобразовывались в абсолютные на этапе конвертации из Atom. Пример приведен в листинге 7.

Листинг 7. Результат преобразования фрагмента XHTML из листинга 6 в формат JSON
"content":{ 
  "attributes":{ 
    "type":"xhtml" 
  }, 
  "children":[ 
    { 
      "name":"img", 
      "attributes":{ 
        "src":"http://example.org/images/foo.jpg" 
      }, 
      "children":[] 
    } 
  ] 
}

Разумеется, для этого необходимо, чтобы приложение, генерирующее фрагменты JSON, могло корректно анализировать разметку HTML и XHTML и выделять из нее атрибуты, содержащие URI.


Повторяемые элементы

Некоторые стандартные элементы Atom могут использоваться многократно внутри одной ленты или одной записи ленты. В JSON они представляются в виде массивов, как показано в листингах 8 и 9.

Листинг 8. Элементы feed, entry и source могут включать несколько дочерних элементов atom:author
<author> 
  <name>James M Snell</name> 
</author> 
<author> 
  <name>Jane Doe</name> 
</author>
Листинг 9. Результат преобразования элементов author из листинга 8 в формат JSON
"authors":[
  {
    "name":"James M Snell",
  },
  {
    "name":"Jane Doe",
  }
]

Повторяемыми элементами являются vocabulary, the author, contributor, category, link и entry. Преобразование подобных элементов в массивы JSON иллюстрируется в листингах с 10-го по 13-й. Все примеры достаточно очевидны и не требуют комментариев.

Листинг 10. Пример ленты Atom, содержащей несколько элементов category
<category term="foo" 
  scheme="http://example.org/categories/" />
<category term="bar" 
  scheme="http://example.org/categories/" 
  label="Bar" />
Листинг 11. Результат преобразования элементов category из листинга 10 в формат JSON
"categories":[
  {
    "term":"foo",
    "scheme":"http://example.org/categories/"
  },
  {
    "term":"bar",
    "scheme":"http://example.org/categories/", 
    "label":"Bar"
  }
]
Листинг 12. Лента Atom, содержащая несколько элементов link
<link href="/foo" 
      rel="related" 
      title="Related Entry"
      type="text/html" />
<link href="/bar" 
      title=”Alternate View"
      type="text/html" />
Листинг 13. Результат преобразования элементов link из листинга 12 в формат JSON
"links":[
  {
    "href":"http://example.org/foo",
    "rel":"related",
    "title":"Related Entry",
    "type":"text/html"
  },
  {
    "href":"http://example.com/bar",
    "title":"Alternate View",
    "type":"text/html"
  }
]

Даты

Для представления дат и времени в Atom используется подмножество стандарта ISO 8601, описанное в RFC 3339. Пример представления даты внутри элемента update показан в листинге 14.

Листинг 14. Элемент Atom updated, содержащий дату и время в формате RFC 3339
<updated>2007-10-14T12:12:12Z</updated>

Модель данных JSON накладывает ряд ограничений на представление дат, поэтому только три варианта остаются доступными для выбора:

  • Представлять дату в виде числа секунд, прошедших с полуночи 1-го января 1970 г. (часовой пояс UTC), как это принято делать в системах семейства ®.
  • Представлять дату в стандартном формате JavaScript, как это делает функция toString. Примером такого формата может служить Sun Oct 21 2007 12:34:28 GMT-0700 (PDT)
  • Копировать дату в том формате, в котором она представлена в документе Atom

Преимуществом первых двух способов является то, что с данными можно будет работать в JavaScript, не прикладывая дополнительных усилий для преобразования в нужный формат. Другими словами, для создания JavaScript-объекта типа Date достаточно просто вызвать конструктор Date(feed.updated). Но при этом существуют и недостатки. В частности, в первом случае существует опасность потери важных данных, а именно числа миллисекунд и часового пояса. При использовании второго способа неудобства доставляют различия в поведении функции toString при выполнении в разных браузерах и при разных региональных настройках. Поэтому, несмотря на то что использование сериализации в формате RFC 3339 требует определенных дополнительных усилий, это единственный способ гарантированно избежать потери данных или неоднозначностей при их преобразовании.

Листинг 15. Результат преобразования элемента updated из листинга 14 в формат JSON
"updated":"2007-10-14T12:12:12.000Z"

Код на JavaScript в листинге RFC 3339 разбирает строку в формате RFC 3339 и создает корректный JavaScript-объект типа Date.

Листинг 16. Фрагмент кода на JavaScript для разбора значений типа "дата-время", содержащихся в лентах Atom в формате RFC 3339
AtomDate = Class.create(); 
AtomDate.pattern = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?(?:[Tt]   \
  (\d{2}):(\d{2}):(\d{2})(?:\.(\d*))?)?([Zz])?(?:([+-])(\d{2}):(\d{2}))?$/; 
AtomDate.localoffset = (new Date()).getTimezoneOffset(); 
AtomDate.padding = function(val,char,count) { 
  var value = ""; 
  while(count > 0) { 
    if (val < Math.pow(10,count)) value = char + value; 
    count--; 
  } 
  return value + val; 
} 
AtomDate.parse = function(val) { 
  if (!val) throw "Invalid Date"; 
  if (val instanceof Date) return val; 
  var m = AtomDate.pattern.exec(val); 
  var year = new Number(m[1]?m[1]:0); 
  var month = new Number(m[2]?m[2]:0); 
  var day = new Number(m[3]?m[3]:0); 
  var hour = new Number(m[4]?m[4]:0); 
  var minute = new Number(m[5]?m[5]:0); 
  var second = new Number(m[6]?m[6]:0); 
  var millis = new Number(m[7]?m[7]:0); 
  var gmt = m[8]; 
  var dir = m[9]; 
  var offhour = new Number(m[10]?m[10]:0); 
  var offmin = new Number(m[11]?m[11]:0); 
  
  if (dir && offhour && offmin) { 
    var offset = ((offhour * 60) + offmin); 
    if (dir == "+") { 
      minute -= offset; 
    } else if (dir == "-") { 
      minute += offset; 
    } 
  } 
  return new Date(Date.UTC(year,month,day,hour,minute,second,millis)); 
}

Object.prototype.value2date = function() { 
  return AtomDate.parse(this); 
}

С помощью функции value2date можно создавать объекты Date из любых строк, содержащих текстовые представления дат в формате RFC 3339.

Замечание: регулярное выражение AtomDate.pattern разбито на несколько строк для удобства восприятия.

Листинг 17. Пример использования функции из листинга 16 для разбора строкового представления даты
document.write(m.updated.value2date());

Текстовые конструкции

Основная сложность при преобразовании в JSON заключается в текстовых конструкциях благодаря широкому набору вариантов их представления в документах Atom. Фрагменты текста, в частности содержимое элементов title, subtitle, summary или rights, могут содержать обычный текст, HTML в escape-кодировке или разметку XHTML. Кроме того, необходимо принимать во внимание используемый язык, указанный в атрибуте xml:lang, а также преобразовывать встречающиеся в тексте относительные URL в абсолютные. Наконец, источником дополнительных трудностей является элемент content, который может содержать данные в кодировке Base64, произвольные фрагменты XML, а также ссылаться на внешние ресурсы с помощью атрибута src.

Основной целью преобразования документов Atom в JSON является разработка такого представления, которое обеспечивало бы максимальную поддержку текстовых конструкций. В листинге 18 показан пример ленты, содержащей три текстовых фрагмента.

Листинг 18. Элемент feed, содержащий три разных типа текстовых конструкций
<feed xmlns="http://www.w3.org/2005/Atom"> 
  ...
  <title>Example Feed</title>
  <subtitle type="html"><p>This is an example feed</p></subtitle>
  <rights xml:lang="fr">...</right>
  ...
</feed>

Содержимое элемента title рассматривается как обычный текст. Его единственной характеристикой является язык, унаследованный от родительского элемента feed. Элемент subtitle содержит фрагмент HTML в escape-кодировке. Наконец, в элементе rights вновь содержится обычный текст, но при этом переопределяется языковый контекст. Пример сериализации всех трех элементов в формат JSON показан в листинге 19.

Листинг 19. Результат преобразования текстовых конструкций из листинга 18 в формат JSON
{
  "title":"Example Feed", 
  "subtitle":{ 
    "attributes":{ 
      "type":"html" 
    }, 
    "children":[
      { 
        "name":"p", 
        "attributes":{ }, 
        "children":["This is an example feed" ] 
      } 
    ]
  },
  "rights":{
    "attributes":{
      "lang":"fr"
    },
    "children":[
      "..."
    ]
  } 
}

Проще всего выглядит сериализация элемента title: его содержимое представляется в виде обычной строки. Значением элемента rights также является простой текст, однако ввиду переопределения языкового контекста приходится создавать объект, состоящий из двух полей – attributes и children.

Содержимое элемента subtitle, представляющее собой фрагмент HTML, разбирается и трансформируется в иерархическую структуру. Аналогичная структура используется для разметки XHTML (листинги 20 и 21).

Листинг 20. Текст, содержащий разметку XHTML
<subtitle type="xhtml">
  <div xmlns="..."><p>This is an example feed</p></div>
</subtitle>
Листинг 21. Результат преобразования фрагмента XHTML из листинга 20 в формат JSON
"subtitle":{ 
  "attributes":{ "type":"xhtml" }, 
  "children":[
    { 
      "name":"p", 
      "attributes":{ }, 
      "children":["This is an example feed" ] 
    }
  ] 
}

Целью данной структуры является обеспечение согласованности и однозначности при сериализации фрагментов HTML и XHTML. При этом существует один недостаток, а именно, для корректного отображения в Web-браузере недостаточно просто вызвать document.write(...) или установить значение свойства innerHTML объекта div. Поэтому необходимо преобразовать фрагмент, содержащийся в данной структуре, обратно в представление, подходящее для вывода на экран.

В листинге 22 приведен модифицированный фрагмент кода на JavaScript, который был изначально создан Сэмом Руби (Sam Ruby), для преобразования текстовых конструкций Atom в формат JSON и обратно в строковое представление.

Листинг 22. Фрагмент кода на JavaScript для преобразования текстовых конструкций в формате JSON в строки для вывода на экран
Array.prototype.hash2value = function () { 
  var result = ''; 
  for (var i=0; this.length>=i; i++) if (this[i]) result+=this[i].tag2value(); 
  return result; 
} 

Object.prototype.tag2value = function () { 
  if (this.name) { 
    var result = String.fromCharCode(60) + this.name; 
    for (key in this.attributes) { 
      if (typeof(this.attributes[key]) === 'function') continue; 
      result += ' ' + key + '="' + this.attributes[key].toString() + '"'; 
    } 
    result += '>' + this.children.hash2value(); 
    result += String.fromCharCode(60) + '/' + this.name + '>'; 
    return result; 
  } else return this.toString(); 
}

Object.prototype.value = function() { 
  if (this.children) return this.children.hash2value(); 
  else return this.toString(); 
} 

Array.prototype.value = function() { 
  var result = ''; 
  for (var i = 0; this.length>=i; i++) 
    if (this[i]) result+=this[i].value(); 
  return result; 
}

Благодаря функциям value, добавленным к JavaScript-классам Array и Object можно вызывать функцию hash2value, не заботясь о том, сериализуется ли данный объект в виде простой строки или объектной структуры.

Листинг 23. Пример использования кода на JavaScript из листинга 22
document.write(m.title.value()); 
document.write(m.subtitle.value()); 
document.write(m.rights.value());

Используя данный подход, можно работать с любым типом содержимого элемента atom:content так же, как с обычным текстом или фрагментами HTML и XHTML. В листинге 24 приведены четыре примера, иллюстрирующие использование различных медиа-типов, а именно: произвольного корректно-сформированного фрагмента XML, двоичных данных в кодировке Base64 и внешнего фрагмента содержимого, на который указывает значение атрибута src.

Листинг 24. Примеры различных типов содержимого элемента content
<content type="text/plain">This is plain text</content>

<content type="application/xml"><a xmlns="foo">b</a></content>

<content type="image/jpg">{base64 encoded data}</content>

<content type="image/jpg" src="image.jpg" />

В листинге 25 показаны все три варианта содержимого после сериализации в формат JSON.

Листинг 25. Результат преобразования содержимого элементов content из листинга 24 в формат JSON
"content":{ 
  "attributes":{ "type":"text/plain" }, 
  "children":["This is plain text" ] 
} 

"content":{
  "attributes":{ "type":"application/xml" },
  "children":[
    {
      "name":"a",
      "attributes": {
        "xmlns":"foo"
      },
      "children":["b"]
    }
  ]
}

"content":{ 
  "attributes":{ "type":"image/jpg" }, 
  "children":["{base64 encoded data}" ] 
} 

"content":{ 
  "attributes":{ 
    "type":"image/jpg", 
    "src":"http://example.org/image.jpg" 
  }, 
  "children":["" ] 
}

Расширения

Последним аспектом преобразования документов Atom в формат JSON, которому необходимо уделить внимание, являются расширения Atom. Существуют три подхода:

  • Игнорировать расширения и не включать их в итоговое представление данных в формате JSON
  • Преобразовывать известные расширения и игнорировать остальные
  • Преобразовывать все расширения

Очевидно, что первый вариант наиболее прост, однако он накладывает ограничения на представления данных в JSON (хотя это далеко не всегда плохо). Второй вариант позволяет оптимизировать и упростить сериализацию известных расширений, но также ограничивает итоговое представление. В третьем случае серьезно усложняется процесс сериализации, однако этот вариант позволяет перенести всю информацию из исходного документа Atom в представление JSON.

Для начала рассмотрим пример того, как может быть оптимизирован результат сериализации известного расширения. Расширение Atom для представления обсуждений (Atom Threading Extension), описанное в документе RFC 4685, служит для индикации того, что одна запись является ответом на другую. В спецификации данного расширения четко описаны атрибуты и семантика элемента in-reply-to. Также указано, что этот элемент может многократно повторяться внутри элементов entry. Зная об этом, можно создать оптимизированное JSON-представление данного атрибута. Пример приведен в листингах 26 и 27.

Листинг 26. Использование в элементе entry расширения Atom для представления обсуждений
<entry>
  ...
  <thr:in-reply-to ref="tag:example.org,2007:/foo/entries/2" />
  <thr:in-reply-to ref="tag:example.org,2007:/foo/entries/3" />
  ...
</entry>
Листинг 27. Результат преобразования расширения из листинга 26 в формат JSON
 "inreplyto":[
   { 
     "ref":"tag:example.org,2007:/foo/entries/2" 
   },
   {
     "ref":"tag:example.org,2007:/foo/entries/3"
   }
 ]

В случае сериализации неизвестных расширений приходится использовать более подробный синтаксис, чтобы учесть все необходимые детали. В листинге 28 показан пример элемента entry, содержащего элемент extension сложной и неизвестной структуры.

Листинг 28. Использование сложного и неизвестного расширения в элементе entry
<entry>
  ...
  <foo:a xmlns="..."><foo:b><foo:c d="e">f</foo:c></foo:b></foo:a>
  ...
</entry>

Сериализация подобных расширений, как правило, аналогична преобразованию текстовой структуры, содержащей фрагмент HTML или XHTML. Пример показан в листинге 29.

Листинг 29. Результат преобразования расширения из листинга 28 в формат JSON
"extensions":[
  {
    "name":"foo:a", 
    "attributes":{ 
      "xmlns:foo":"http://example.org/unknown-markup" 
    }, 
    "children":[
      { 
        "name":"foo:b", 
        "attributes":{ }, 
        "children":[
          { 
            "name":"foo:c", 
            "attributes":{ "d":"e" }, 
            "children":["f" ]
          }
        ]
      }
    ]
  }
]

Собирая все части воедино

На основе всего сказанного выше можно написать процедуру преобразования любого документа Atom в формат JSON, пригодный для дальнейшего использования. Сам процесс конвертации иллюстрируется в листингах 30 и 31. Исходный документ Atom содержит относительные IRI, языковые контексты, расширения, различные виды текстовых конструкций и т.д. При этом, использовав доступные в Интернет конвертеры из XML в JSON, вы получите различные представления JSON, но в любом случае либо возникнет проблема потери информации при преобразовании, либо появятся сложности, связанные с удобством дальнейшего использования представления, либо и то, и другое.

Листинг 30. Пример полной версии документа Atom
<?xml version="1.0" encoding="utf-8" ?> 
<a:feed xmlns:a="http://www.w3.org/2005/Atom" 
        xmlns:thr="http://purl.org/syndication/thread/1.0" 
        xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:foo="http://example.org/unknown-markup" 
        xml:lang="en-US" 
        xml:base="http://example.org/foo" 
        dir="ltr"> 
  
  <a:id>tag:example.org,2007:/foo</a:id> 
  <a:title>Example Feed</a:title> 
  <a:subtitle type="html"><![CDATA[<p>This is an example feed</p>]]></a:subtitle> 
  <a:rights type="xhtml">
    <div>
      <p>Copyright © James M Snell</p>
    </div>
  </a:rights> 
  <a:author xmlns="http://www.w3.org/2005/Atom"> 
    <name>James M Snell</name> 
    <email>jasnell@example.org</email> 
    <uri>/~jasnell</uri> 
  </a:author> 
  <a:updated>2007-10-14T12:12:12Z</a:updated> 
  <a:link rel="self" href="" /> 
  <a:link href="/blog" /> 
  <a:link rel="alternate" type="application/json" href="/blog;json" /> 
  
  <a:entry xml:base="entries/1"> 
    <a:id>tag:example.org,2007:/foo/entries/1</a:id> 
    <a:title type="text">Entry Number One</a:title> 
    <a:summary type="xhtml"> 
      <div> 
        <p>This is the first entry. You can read it <a href="">here</a></p> 
      </div> 
    </a:summary> 
    <a:rights type="html">
      <p>Copyright &copy; James M Snell</p>
    </a:rights> 
    <a:updated>2007-10-14T12:12:12Z</a:updated> 
    <a:link href="" /> 
    <a:link rel="alternate" type="application/json" href="1;json" /> 
    <a:link rel="replies" type="application/atom+xml" 
      href="1;replies" thr:count="10" /> 
    <a:content type="xhtml"> 
      <div> 
        <p>This is the content of the first entry. It contains a picture.</p> 
        <img src="/images/foo.jpg" /> 
      </div> 
    </a:content> 
    <thr:in-reply-to ref="tag:example.org,2007:/foo/entries/2" /> 
    <a:category scheme="http://example.org/categories/" term="foo" 
      label="test" xml:lang="en-US" /> 
    <a:category scheme="http://example.org/categories/" term="bar" 
      label="essai" xml:lang="fr" /> 
    <foo:a><foo:b><foo:c d="e">f</foo:c></foo:b></foo:a> 
  </a:entry> 
 
  <a:entry xml:base="entries/2" xml:lang="fr"> 
    <a:id>tag:example.org,2007:/foo/entries/2</a:id> 
    <a:title type="text">La première entrée</a:title> 
    <a:summary type="xhtml"> 
      <div> 
        <p>Il s'agit de la première entrée. Vous pouvez lire 
        <a href="">est ici</a></p> 
      </div> 
    </a:summary>
    <a:rights type="html">
      <p>Copyright &copy; James M Snell</p>
    </a:rights> 
    <a:updated>2007-10-14T12:12:11Z</a:updated> 
    <a:link href="" /> 
    <a:link rel="alternate" type="application/json" href="2;json" /> 
    <a:link rel="replies" type="application/atom+xml" 
      href="2;replies" thr:count="10" /> 
    <a:content type="xhtml"> 
      <div> 
        <p>Ceci est le contenu de la première entrée. Il contient une image.</p> 
        <img src="/images/foo.jpg" /> 
      </div> 
    </a:content> 
    <thr:in-reply-to ref="tag:example.org,2007:/foo/entries/1" /> 
    <a:category scheme="http://example.org/categories/" term="foo" 
      label="test" xml:lang="en-US" /> 
    <a:category scheme="http://example.org/categories/" term="bar" 
      label="essai" xml:lang="fr" /> 
    <foo:a><foo:b><foo:c d="e">f</foo:c></foo:b></foo:a> 
  </a:entry> 
</a:feed>

Схема сериализации документов Atom позволяет создавать понятные, легкие в использовании фрагменты JSON, избегая при этом потери важных контекстных данных.

Листинг 31. Результат преобразования ленты Atom из листинга 30 в формат JSON
{ 
 "lang":"en-US", 
 "dir":"ltr", 
 "id":"tag:example.org,2007:/foo", 
 "title":"Example Feed", 
 "subtitle":{ 
  "attributes":{ 
   "type":"html" 
  }, 
  "children":[{ 
    "name":"p", 
    "attributes":{ }, 
    "children":["This is an example feed" ] 
   } ] }, 
 "rights":{ 
  "attributes":{ "type":"xhtml" }, 
  "children":[{ 
    "name":"p", 
    "attributes":{ }, 
    "children":["Copyright \u00a9 James M Snell" ] 
   } ]}, 
 "updated":"2007-10-14T12:12:12.000Z", 
 "authors":[{ 
   "name":"James M Snell", 
   "email":"jasnell@example.org", 
   "uri":"http://example.org/~jasnell" 
  } ], 
 "links":[
   { 
     "href":"http://example.org/foo", 
     "rel":"self" 
   },
   { 
     "href":"http://example.org/blog" 
   },
   { 
     "href":"http://example.org/blog;json", 
     "rel":"alternate", 
     "type":"application/json" 
   } ], 
 "entries":[...],
 "attributes":{ 
  "xml:lang":"en-US", 
  "xml:base":"http://example.org/foo" 
 }
}

Использование Abdera для представления данных в JSON

Схема преобразования, описанная в этой статье, была реализована в проекте Apache Abdera. Пример использования класса Abdera для записи данных в формате JSON приведен в листинге 32. Если вам интересно поэкспериментировать с преобразованием данных из Atom в JSON, то посетите страницы Wiki проекта Abdera, где есть ссылка на наиболее свежую сборку проекта.

Листинг 32. Использование Apache Abdera для записи данных в формате JSON
Abdera abdera = new Abdera();
Entry entry = abdera.newEntry();
    
entry.setId("http://example.org");
entry.setTitle("Testing the JSON Writer");
entry.setUpdated(new Date());
entry.addLink("http://www.example.org");
entry.addAuthor("James Snell");
entry.setSummary("This is a test of the JSON Writer");
    
entry.writeTo("json", System.out);

Резюме

Перевод данных из одного формата в другой никогда не бывает простой задачей, а если речь идет о таком гибком и мощном стандарте, как Atom и преобразовании в столь простой и базовый формат, как JSON, то ряд проблем при конвертации – это лучшее, что можно ожидать. Несмотря на несколько достаточно удачных попыток создания корректных конвертеров, не существует единого стандарта на подобные преобразования. Поэтому разработчики приложений должны быть готовы к тому, что придется иметь дело с несколькими вариантами сериализации JSON, которые сильно отличаются по качеству и могут быть несовместимы. В этой статье был рассмотрен один из возможных подходов к преобразованию документов Atom в формат JSON.

Ресурсы

Научиться

  • Оригинал статьи: “Convert Atom documents to JSON” (Боб дю Шарм, developerWorks, январь 2008 г.). (EN)
  • Прочитайте о формате синдицирования Atom, описанном в документе RFC 4287. Данный формат основан на XML и предназначен для описания лент, которые агрегируют Web-контент, например записи в Web-блогах или новостные заголовки, и передают его напрямую пользовательским программам. (EN)
  • Прочитайте документ RFC 4627, разработанный IETF, в котором описывается формат JSON. (EN)
  • Обратитесь к страницам Wiki проекта Apache Abdera, которые содержат информацию как о самом проекте, так и о разработке Java-приложений, работающих с лентами Atom. (EN)
  • Ознакомьтесь с API GData от Google, представляющим собой альтернативный подход к конвертированию документов Atom в формат JSON. (EN)
  • Обратитесь к технической библиотеке XML, содержащей множество статей, советов, руководств, стандартов и справочников IBM Redbook. (EN)
  • Технические мероприятия и Web-трансляции developerWorks: в этих разделах можно получить самую актуальную информацию о современных технологиях. (EN)
  • Обратитесь к магазину технической литературы, в котором представлены книги на данную и другие темы. (EN)

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML, Open source
ArticleID=467247
ArticleTitle=Преобразование документов Atom в формат JSON
publish-date=02092010