Semantic Web, связанные данные и Drupal: Часть 1. Публикация данных с помощью RDF

В этой статье рассказывается о том, как сделать данные более удобными для взаимодействия и повысить эффективность обмена ими. На примере иллюстрируется, как использовать Drupal 7 для публикации связанных данных, отображая контент с помощью RDF.

Лин Кларк, Drupal-разработчик, Digital Enterprise Research Institute, NUI Galway

Фото автораЛин Кларк (Lin Clark) – Drupal-разработчик, специализирующийся на связанных данных. Участвовала в инициативе по внедрению RDF в ядро Drupal 7, создала инструмент SPARQL Views в рамках программы "Лето программирования Google-2010" и пропагандирует преимущества использования технологий связанных данных в приложениях для решения повседневных задач. Училась в Карнеги-Меллонском университете и в настоящее время работает над диссертацией в Научно-исследовательском институте Цифрового Предприятия при Ирландском национальном университете (Голуэй). Более подробную информацию о Лин Кларк можно получить на сайте lin-clark.com.



17.10.2011

Введение

В 2001 году в статье The Semantic Web, ставшей легендарной, сэр Тим Бернерс-Ли описал мир, в котором агент карманного устройства обменивается данными с другими агентами и принимает решения, упрощая жизнь пользователю. С тех пор проект Semantic Web получил некоторое развитие и в последние годы принял более прагматичную форму в связи с инициативой "связанных данных", направленной на исследования и разработки в той сфере, которую Бернерс-Ли называет самой важной частью головоломки: совместимости данных.

Технологиями связанных данных можно пользоваться и не прибегая к серьезному программированию. В этой статье мы рассмотрим те широкие возможности в области совместимости данных, которые обеспечивает Drupal 7. Мы покажем, как представить свои интернет-данные с помощью Resource Description Framework (RDF). Вы можете загрузить исходный код, используемый в этой статье.

Предварительные требования

Чтобы следовать примеру из этой статьи, создайте сайт Drupal 7. Для работы Drupal требуется PHP и сервер баз данных, такой как MySQL.


Совместимость данных

Сегодня большая часть информации в Интернете несовместима. Например, если нужно взять данные с одного сайта и соединить их с данными другого сайта, придется написать специальный поисковый агент, который извлечет нужную информацию. Это тем более необходимо, если вы хотите использовать информацию с малобюджетных сайтов ― сайтов физических лиц, госучреждений или учебных заведений. Когда разработчикам предоставляется доступ к структурированной информации сайта, он часто организован через специальные API, которые отличаются от одного сайта к другому.

Инициатива связанных данных использует узкий срез технологий и концепций Semantic Web (таких как RDF), чтобы попытаться решить проблему совместимости и облегчить многократное использование и комбинирование интернет-данных.

С появлением акцента на совместимость данных темпы разработки и инноваций возросли, так как компании осознали возможности технологий Semantic Web. Например, Google использует RDF в атрибутах (RDFa) для получения Rich Snippets - представлений фрагментов, совместимых с алгоритмами Google, в которых выделены структурированные данные, внедренные в Web-страницы. Они повышают ценность результатов поиска, выделяя фрагменты текста, как показано на рисунке 1. Различные источники утверждают, что улучшенные таким образом результаты поиска повышают коэффициент эффективности баннеров на 15-30%.

Рисунок 1. Использование технологии Rich Snippet для рецепта в результатах поиска Google
Google Rich Snippet выделяет элементы текста

Facebook использует RDFa с 2010 года для таких элементов, как кнопки, которые разработчики могут размещать на своих сайтах. Достаточно добавить к Web-странице немного RDFa, чтобы сделать ее эквивалентом страницы Facebook. Когда посетители нажимают кнопку Like, между посетителем и внешней страницей устанавливается Facebook-соединение. Такие возможности взаимодействия – результат соблюдения нескольких простых принципов и технологий Semantic Web.


RDF, словари и принципы связанных данных

Самый важный принцип связанных данных состоит в использовании для Web-объектов отличительных имен, вместо серийного номера или других идентификаторов. Простым способом создания отличительных имен является использование системы имен доменов. Например, если вы предлагаете на своем сайте информацию о некоей Джейн Доу, то ее будет очень трудно отделить от информации о других женщинах с таким же именем. Зато нужную Джейн Доу легко найти с помощью идентификатора http://example.com/people/jane-doe. Такой идентификатор называется HTTP URI.

При использовании HTTP URI для выявления тех или иных объектов можно применять также архитектуру Web, чтобы сделать информацию о Джейн Доу более доступной. Посетителям http://example.com/people/jane-doe можно предоставить дополнительную информацию о Джейн, такую как ее полное имя, учетные записи в Интернете, местожительство или публикации.

Предоставление данных с помощью RDF и словарей

Тем, кто уже работал с базами данных или объектно-ориентированными языками, RDF покажется знакомым. Это всего лишь модель объект-атрибут-значение (хотя в RDF атрибуты часто называют свойствами), как показано в таблице 1.

Таблица 1. Модель "сущность-атрибут-значение"
СущностьСвойствоЗначение
http://example.com/people/jane-doeимяДжейн Доу

Чтобы обеспечить совместимость, необходимо также использовать имена свойств, которые всем понятны, поэтому для свойств тоже используются URI. Наборы URI-свойств публикуются в так называемых словарях (или, более официально, онтологиях).

Ниже приведены примеры словарей.

Friend of a Friend (FOAF)
Предоставляет свойства для описания людей: name, homepage, mbox (email), account, based_near
Dublin Core (DC)
предоставляет свойства для описания опубликованных работ: abstract, created, dateCopyrighted, publisher
Semantically-Interlinked Online Communities (SIOC)
предоставляет свойства для описания онлайновых социальных сетей и их пользователей: follows, has_reply, last_reply_date, moderator_of, subscriber_of

Подробнее о словарях см. в разделе Ресурсы.

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

<http://example.com/people/jane-doe> <http://xmlns.com/foaf/0.1/name> "Jane Doe"

Полное значение URI заключается в угловые скобки, а буквальные значения ― в кавычки.

Можно несколько облегчить чтение с помощью компактных URI (CURIE), определив значение префиксов таких CURIE. Пример приведен в листинге 1.

Листинг 1. RDF-оператора с использованием CURIE
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX person: <http://example.com/people/>

person:jane-doe foaf:name "Jane Doe"

Таким образом, Web-архитектура становится своего рода структурой базы данных. Но вместо того чтобы лежать в общем хранилище данных, все эти данные могут находиться на разных независимых сайтах, и по мере необходимости их можно легко собирать вместе и компоновать.

Для извлечения данных из этого RDF-набора можно использовать язык запросов SPARQL.


Использование SPARQL для запросов DBpedia

Многие наборы данных и информационные центры, такие как Wikipedia, открываются с помощью связанных данных. Хотя значительная часть содержания Википедии – это произвольный текст, который трудно представить в виде связанных данных, в инфобоксах, расположенных в правой части многих страниц Википедии, много структурированной информации. Инфобоксы могут содержать такую информацию, как население города или жанр произведения и псевдоним автора.

DBpedia ― это результат коллективной работы по извлечению из Википедии структурированной информации и предоставлению к ней Web-доступа путем снабжения этой информации RDF. Весь RDF-файл можно загрузить в дамп для использования в приложениях. Также имеется демонстрационный интерфейс запросов.

Простой способ приступить к изучению данных DBpedia - найти URI DBpedia, связанный с определенной темой. Например, предположим, что вы хотите узнать население или получить другие сведения о Питтсбурге. Сначала найдем URL нужной страницы в Википедии, а затем используем запрос SPARQL, приведенный в листинге 2, чтобы найти URI DBpedia, связанный с этой страницей. Так как пример интерфейса Snorql уже содержит префиксы, можно использовать CURIE, не объявляя префиксов.

Листинг 2. Запрос для поиска URI DBpedia Питтсбурга
SELECT ?uri WHERE {
  ?uri foaf:page <http://en.wikipedia.org/wiki/Pittsburgh>
}

URI будет <http://dbpedia.org/resource/Pittsburgh> (хотя интерфейс Snorql просто выдает ссылку на другой запрос, как показано в листинге 3 ).

Листинг 3. Запрос для поиска всей информации о Питтсбурге в DBpedia

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

Если нужно извлечь из DBpedia только один факт – например, население города, - можно использовать запрос, приведенный в листинге 4.

Листинг 4. Запрос для определения населения Питтсбурга
SELECT ?population WHERE {
<http://dbpedia.org/resource/Pittsburgh> dbpedia2:populationMetro ?population
}

Также можно находить дополнительные факты, прослеживая различные связи. Для дальнейшего изучения получим список людей, родившихся в Питтсбурге. Добавив префикс dbo, вместо dbpedia:ontology/birthPlace можно написать dbo:birthPlace, как показано в листинге 5.

Листинг 5. Запрос на получение списка уроженцев Питтсбурга
PREFIX dbo: <http://dbpedia.org/ontology/>

SELECT ?person WHERE {
?person dbo:birthPlace <http://dbpedia.org/resource/Pittsburgh> .
}

Теперь у нас есть список всех уроженцев Питтсбурга. Чтобы сделать его интереснее, отфильтруем этот список по некоторым критериям. Например, выберем тех, кто относится к категории американских блоггеров. Для этого используем специальное свойство rdf:type, как показано в листинге 6.

Листинг 6. Запросов для поиска уроженцев Питтсбурга - американских блоггеров
PREFIX dbo: <http://dbpedia.org/ontology/>

SELECT ?person WHERE {
?person dbo:birthPlace <http://dbpedia.org/resource/Pittsburgh> ;
        rdf:type <http://dbpedia.org/class/yago/AmericanBloggers> .
}

Можно получить более подробную информацию о списке блогеров, родившихся в Питтсбурге. В листинге 7 показано, как получить аннотацию статьи, которая может служить краткой биографией.

Листинг 7. Запрос для получения резюме американских блоггеров, родившихся в Питтсбурге
PREFIX dbo: <http://dbpedia.org/ontology/>

SELECT ?person ?bio WHERE {
?person dbo:birthPlace <http://dbpedia.org/resource/Pittsburgh> ;
        rdf:type <http://dbpedia.org/class/yago/AmericanBloggers> ;
        dbo:abstract ?bio .
}

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

Листинг 8. Запрос резюме на английском языке
PREFIX dbo: <http://dbpedia.org/ontology/>

SELECT ?person ?bio WHERE {
?person dbo:birthPlace <http://dbpedia.org/resource/Pittsburgh> ;
        rdf:type <http://dbpedia.org/class/yago/AmericanBloggers> ;
        dbo:abstract ?bio .
FILTER langMatches( lang(?bio), "en" )
}

Этот запрос возвращает только английскую версию резюме.

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


Поддержка RDF в Drupal 7

Drupal предоставляет широкие возможности в области совместимости для издателей небольших объемов данных. Это система управления контентом с открытым исходным кодом, которая облегчает разработчикам и конечным пользователям создание сложных форм ввода данных. Эти формы идеально подходят для сбора структурированных данных и их гибкого форматирования.

С применением Drupal 7 даже небольшие сайты могут публиковать свои данные с RDF. По умолчанию в RDF попадают главная страница и содержание статей любого нового сайта Drupal 7. Ключом к этому служит API RDF Mapping. С помощью этого API любое поле формы можно поставить в соответствие свойству RDF, а контент любого типа ― типу RDF. Например, если на сайте есть списки исполнителей музыкальных произведений, контент может иметь тип mo:MusicArtist. Поля можно сопоставить с mo:fanpage, mo:discography, mo:biography и т.д.

Контент любого типа, имеющий определенное отображение, автоматически становится доступным с использованием RDFa, т.е. RDF в атрибутах HTML. Например, в RDFa применявшийся выше сценарий с Джейн Доу будет выглядеть, как показано в листинге 9.

Листинг 9. Оператор из листинга 1, выраженный в RDFa
<div about="http://example.com/people/jane-doe">
  <h1 property="foaf:name">Jane Doe</h1>
</div>

Drupal сам заботится о форматировании, что значительно облегчает публикацию правильных RDFa. Теперь владельцы сайтов могут обмениваться данными друг с другом. Легко воспользоваться и такими возможностями, как Rich Snippets из Google.


Настройка специальных отображений RDF в Drupal

В этом разделе мы приступим к публикации связанных данных. Пример создает контент типа Recipe для публикации кулинарных рецептов, которые можно отображать как фрагменты Google Rich Snippets.

Приступаем к работе

Чтобы следовать примеру из этой статьи, создайте сайт Drupal 7. Для работы Drupal требуется PHP и сервер баз данных, такой как MySQL. Если у вас уже есть PHP и установлен сервер баз данных, двигайтесь вперед согласно инструкциям руководства по установке. В противном случае Acquia предоставляет простой в применении Acquia Stack Installer, который устанавливает Web-сервер Apache, PHP, сервер MySQL и сайт Drupal 7. Более подробные сведения можно получить по ссылкам из раздела Ресурсы.

Если сайт готов, попробуйте добавить статью, используя кнопку Add content на второй панели инструментов. Новый контент должен появиться в списке на первой странице. Посмотрите на исходный код и заметьте, что в HTML включен RDFa. Например, автор статьи помечен тегом sioc:has_creator, поскольку контент типа Page и Article по умолчанию имеет отображение RDF.

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

Создание модуля

Первым требованием для модуля Drupal является наличие файла .info, как показано в листинге 10. Он передает системе информацию о файлах, содержащихся в модуле, и о других модулях, от которых они зависят.

Листинг 10. Файл rdf_example.info
name = RDF Example
description = Demonstrates an RDF mapping using the RDF Mapping API.
core = 7.x
dependencies[] = richsnippets 
dependencies[] = field_collection

Массив зависимостей указывает Drupal, какие другие модули должны быть установлены, прежде чем можно будет установить этот. Обратите внимание, что сам модуль field_collection зависит от Entity API. Для загрузки этих модулей см. ссылки в разделе Ресурсы.

В примере добавлена зависимость от модуля Rich Snippets, потому что в RDFa для Google нужно внести незначительные корректировки. Добавлена зависимость от field_collection, потому что он будет использоваться для создания групп полей, таких как информация о блюде или ингредиентах.

Создание типа контента

Следующий шаг заключается в создании типа контента и внесении соответствующих полей в файл .install. Файл .install состоит из трех функций, как описано ниже.

ФункцияНазначение
rdf_example_installРеализует модуль hook_install и вызывается, когда он установлен.
_rdf_example_installed_fieldsЧастная функция, которая вызывается модулем rdf_example_install; обеспечивает определения для нужных полей.
_rdf_example_installed_instancesДругая частная функция, вызываемая модулем rdf_example_install; она определяет, какие поля к каким типам контента относить.

В модуле rdf_example_install сначала создайте узел типа Recipe, как показано в листинге 11.

Листинг 11. Определение типа узла в rdf_example_install
  // Определение типа узла.
  $rdf_example = array(
    'type' => 'recipe',
    'name' => $t('Recipe'),
    'base' => 'node_content',
    'description' => $t('The recipe node is defined to demonstrate RDF mapping.'),
  );

  // Установка дополнительных значений по умолчанию и сохранение типа контента.
  $content_type = node_type_set_defaults($rdf_example);
  node_type_save($content_type);

Создайте поля, которые можно присоединить к узлу типа Recipe, как показано в листинге 12. Эти поля могут использоваться и другими модулями или пользователями с помощью интерфейса Fields. Для сохранения чистоты кода определения полей хранятся в другой функции (_rdf_example_installed_fields).

Листинг 12. Создание полей, которые можно добавлять к типу узла
  foreach (_rdf_example_installed_fields() as $field) {
    field_create_field($field);
  }

Предыдущий код вызывает функцию _rdf_example_installed_fields , чтобы получить список полей, которые нужно установить. Определения содержат имена и типы полей, а также их мощность (количество значений, которые может иметь данное поле). Для некоторых полей настраиваются дополнительные параметры. Чтобы определить поля, пример просто создает все поля, которые будут использоваться. Вам не нужно беспокоиться о типе контента к которому они будут прикреплены.

В листинге 13 обратите внимание на то, что recipe_nutrition - это поле field_collection, которое представляет собой специальный тип. Оно связано с объектом field_collection, который может иметь свой собственный RDF-тип и содержать собственные поля. Для recipe_nutrition тип объекта создается автоматически. На следующем шаге прикрепим к нему поля, как показано в листинге 14.

Листинг 13. Определение полей в _rdf_example_installed_fields()
array(
    'recipe_photo' => array(
      'field_name' => 'recipe_photo',
      'cardinality' => 1,
      'type'        => 'image',
    ),
    'recipe_summary' => array(
      'field_name'  => 'recipe_summary',
      'cardinality' => 1,
      'type'        => 'text',
      'settings'    => array(
        'max_length' => 500,
      ),
    ),
    'recipe_nutrition' => array(
      'field_name'  => 'recipe_nutrition',
      'cardinality' => 1,
      'type'        => 'field_collection',
    ),
    'recipe_serving_size' => array(
      'field_name'  => 'recipe_serving_size',
      'cardinality' => 1,
      'type'        => 'text',
    ),
    'recipe_calories' => array(
      'field_name'  => 'recipe_calories',
      'cardinality' => 1,
      'type'        => 'number_integer',
    ),
  );

Теперь, когда поля в системе созданы, применим экземпляры полей к контенту типа Recipe и к набору полей Recipe Nutrition с помощью field_create_instance. (В Drupal эти типы называются "связками" [bundles].) Напомним, что для чистоты кода определения данного примера хранятся в отдельной функции (_rdf_example_installed_instances). Пример приведен в листинге 14.

Листинг 14. Прикрепление экземпляров полей к типам контента в rdf_example_install
  foreach (_rdf_example_installed_instances() as $bundle_name => $bundle) {
    foreach ($bundle as $instance) {
      $instance['entity_type'] = $bundle_name == 'recipe' ? 'node' : 
'field_collection_item';
      $instance['bundle'] = $bundle_name;
      field_create_instance($instance);
    }
  }

Чтобы получить определения экземпляров полей, приведенный выше код вызывает _rdf_example_installed_instances. В модуле _rdf_example_installed_instances создается массив, отсортированный по типам, с которыми связан пример. В данном случае это либо контент типа Recipe, либо группа полей Recipe Nutrition, как показано в листинге 15.

Листинг 15. Определение экземпляров в модуле _rdf_example_installed_instances()
  $instances = array();
  $instances['recipe'] = array(
    'recipe_photo' => array(
      'field_name'  => 'recipe_photo',
      'label'       => $t('Photo of the prepared dish'),
    ),
    'recipe_summary' => array(
      'field_name' => 'recipe_summary',
      'label'       => $t('Short summary describing the dish'),
      'widget'      => array(
        'type'    => 'text_textarea',
      ),
    ),
    'recipe_nutrition' => array(
      'field_name' => 'recipe_nutrition',
      'label'      => $t('Recipe Nutrition Information'),
    ),
  );
  $instances['recipe_nutrition'] = array(
    'recipe_serving_size' => array(
      'field_name' => 'recipe_serving_size',
      'label'       => $t('Serving size'),
    ),
    'recipe_calories' => array(
      'field_name' => 'recipe_calories',
      'label'       => $t('Calories'),
    )
  );

Отображение контента в RDF

В файле .module создадим отображение RDF для определенных нами полей и типов контента. Так как тип контента определен в этом модуле, для создания RDF-отображения для типа контента используем hook_rdf_mapping. Изменение отображения типа контента, созданного в другом модуле, здесь не показано. Чтобы изменить существующие отображения, в функции install надо использовать функции rdf_mapping_load и rdf_mapping_save, предоставляемые API RDF Mapping.

Так же, как мы это делали при создании экземпляров, организуем этот массив по типу связки. Отображения для контента типа Recipe определяются в в листинге 16, а для набора полей РRecipe Nutrition ― в листинге 17.

Листинг 16. Декларация RDF-отображения рецепта из rdf_example.module
    'recipe' => array(
      'type' => 'node',
      'bundle' => 'recipe',
      'mapping' => array(
        'rdftype' => array('v:Recipe'),
        // Мы не используем отображение связки по умолчанию для заголовка. 
        // Вместо этого, добавим свойство v:name. Однако мы по-прежнему хотим 
        // использовать также и dc:title, поэтому включим в массив и его.
        'title' => array(
          'predicates' => array('dc:title', 'v:name'),
        ),
        'recipe_summary' => array(
          'predicates' => array('v:summary'),
        ),
        // URI  фотографии не является строкой, а указывает на ресурс, поэтому укажем, 
        // что это атрибут типа rel. Если тип не указан, он по умолчанию настраивается на 
        // свойство, используемое для строковых значений.
        'recipe_photo' => array(
          'predicates' => array('v:photo'),
          'type' => 'rel',
        ),
        'recipe_nutrition' => array(
          'predicates' => array('v:nutrition'),
          'type' => 'rel',
        ),
      ),
    ),
Листинг 17. Декларация RDF-отображения блюда из rdf_example.module
    'nutrition' => array(
      'type' => 'field_collection_item',
      'bundle' => 'recipe_nutrition',
      'mapping' => array(
        'rdftype' => array('v:Nutrition'),
        'recipe_serving_size' => array(
          'predicates' => array('v:servingSize'),
        ),
        'recipe_calories' => array(
          'predicates' => array('v:calories'),
        ),
      ),
    ),

Наконец, реализуем hook_rdf_namespaces, так как определение-префикс было включено в начало HTML-документа, как показано в листинге 18.

Листинг 18. Объявление пространства имен RDF из rdf_example.module
function rdf_example_rdf_namespaces() {
  return array(
    // Пространство имен Google для его специальных словарей.
    'v' => 'http://rdf.data-vocabulary.org/#', 
  );
}

Тестирование Rich Snippets

Модуль готов, так что попробуем его установить. Контент типа Recipe должен отображаться на странице добавления контента.

В форме узла Recipe будут доступны заголовок, изображение и поле аннотации. Чтобы увидеть Rich Snippet в инструменте тестирования, нужно добавить изображение. Когда узел передан, можно добавить сведения о блюде. Не забудьте указать, как минимум, его калорийность.

Теперь, когда у нас есть готовый рецепт, проверим его с помощью инструмента Rich Snippets Testing Tool. Вы должны увидеть изображение и калорийность.


Заключение

Технологии связанных данных помогают сделать Web-данные более совместимыми, допускающими многократное использование простыми в работе. Такие компании, как Google и Facebook, активно используют эти технологии, так как они упрощают обмен данными. С помощью Drupal мелкие издатели данных и потребители также могут извлечь выгоду из этих технологий, не прибегая к интенсивному программированию. В этой статье мы рассмотрели, как представить данные своего сайта с помощью RDF.

Во второй части этого цикла мы научимся использовать данные, предоставляемые другими, и совмещать их со своими собственными данными на Web-сайте Drupal.


Загрузка

ОписаниеИмяРазмер
Исходный код примера для статьиrdf_example.zip3 КБ

Ресурсы

Научиться

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

Комментарии

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=SOA и web-сервисы, Web-архитектура
ArticleID=765801
ArticleTitle=Semantic Web, связанные данные и Drupal: Часть 1. Публикация данных с помощью RDF
publish-date=10172011