Поиск по сайту

Login Form



Главная Инструменты Менеджер MP3 файлов на основе RDF и C# - Часть 1
Менеджер MP3 файлов на основе RDF и C# - Часть 1 PDF Печать E-mail
Рейтинг пользователей: / 1
ХудшийЛучший 
Автор: Эндрю Мэттьюс   
09.04.2008 15:24

Эндрю Мэттьюс
Оригинал: Using RDF and C# to Create an MP3 Manager - Part 1

Эта статья является продолжением предыдущей публикации о разработке приложений Semantic Web на C#. Я опять воспользуюсь библиотекой SemWeb, но в этот раз я хочу продемонстрировать возможности RDF на примере простого менеджера MP3 файлов. Я его еще не закончил, и буду работать над ним в течении следующих нескольких дней, для того, чтобы показать вам насколько легко стало работать с RDF/OWL на C# в наши дни.

Программа довольно проста, меня натолкнул на мысль написать ее сайт RDF-izers, где вы можете найти множество инструментов для преобразования данных из разных форматов в RDF. Пока я осваивался с LINQ, я создал простую систему тэгов для файлов, я просто сканировал файлы и извлекал из них все метаданные, которые мог, и сохранял их в базе данных тегов на SQL сервере. Менеджер MP3 файлов не слишком сильно отличается от такой системы. Я просто извлек ID3 тэги из файлов MP3 и сохранил полученную метаинформацию в объектах Track. Затем я написал простой конвертер для того, чтобы сохранить извлеченные данные в RDF хранилище в памяти. Все вместе это заняло у меня 3-4, часа включая поиск подходящего API для чтения ID3. Я не буду показывать (пока не попросят) код для тестирования или для обхода файловой системы. Вместо этого я покажу вам код, который я написал для сохранения объектов в RDF хранилище.

Прежде всего, мы имеем класс Track. Я выкинул большую часть реализации свойств для краткости.

 
[OntologyBaseUri("file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/")]
[OwlClass("Track", true)]
public class Track : OwlInstanceSupertype
{
    [OwlProperty("title", true)]
    public string Title /* … */
    [OwlProperty("artistName", true)]
    public string ArtistName /* … */
    [OwlProperty("albumName", true)]
    public string AlbumName /* … */
    [OwlProperty("year", true)]
    public string Year /* … */
    [OwlProperty("genreName", true)]
    public string GenreName /* … */
    [OwlProperty("comment", true)]
    public string Comment /* … */
    [OwlProperty("fileLocation", true)]
    public string FileLocation /* … */
 
    private string title;
    private string artistName;
    private string albumName;
    private string year;
    private string genreName;
    private string comment;
    private string fileLocation;
 
    public Track(TagHandler th, string fileLocation)
    {
        this.fileLocation = fileLocation;
        title = th.Track;
        artistName = th.Artist;
        albumName = th.Album;
        year = th.Year;
        genreName = th.Genere;
        comment = th.Comment;
    }
}
 

Вообще то здесь нечего комментировать за исключением указания нескольких ключевых атрибутов, которые используются для того, чтобы предоставить механизму сериализации дополнительную информацию о том, как генерировать URI для класса, его свойств и их значений. Очевидно, что это предварительная версия, поэтому мы не указываем большое количество дополнительной информации о типах XSD, версиях и т.д. Но я уверен, что вы уловили идею: мы можем сделать для RDF многое из того, что LINQ для SQL делает в отношении реляционных баз данных.

Классы атрибутов также очень просты:

 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property)]
public class OwlResourceSupertypeAttribute : Attribute
{
    public string Uri
    {
        get { return uri; }
    }
    private readonly string uri;
    public bool IsRelativeUri
    {
        get { return isRelativeUri; }
    }
    private readonly bool isRelativeUri;
    public OwlResourceSupertypeAttribute(string uri)
        : this(uri, false){}
    public OwlResourceSupertypeAttribute(string uri, bool isRelativeUri)
    {
        this.uri = uri;
        this.isRelativeUri = isRelativeUri;
    }
}
 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property)]
public class OwlClassAttribute : OwlResourceSupertypeAttribute
{
    public OwlClassAttribute(string uri)
        : base(uri, false){}
    public OwlClassAttribute(string uri, bool isRelativeUri)
        : base(uri, isRelativeUri){}
}
 
[AttributeUsage(AttributeTargets.Property)]
public class OwlPropertyAttribute : OwlResourceSupertypeAttribute
{
    public OwlPropertyAttribute(string uri)
        : base(uri, false){}
    public OwlPropertyAttribute(string uri, bool isRelativeUri)
        : base(uri, isRelativeUri){}
}
 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class OntologyBaseUriAttribute : Attribute
{
    public string BaseUri
    {
        get { return baseUri; }
    }
    private string baseUri;
    public OntologyBaseUriAttribute(string baseUri)
    {
        this.baseUri = baseUri;
    }
}
 

OwlResourceSupertypeAttribute - это базовый класс для всех атрибутов, имеющих отношение к ресурсам в онтологии, то есть ко всему, что имеет URI. Поэтому он имеет свойство Uri, и кроме того, он имеет свойство isRelativeUri, которое определяет, является ли URI абсолютным или указывается относительно базового URI определенного где-то еще. Хотя я пока не реализовал эту функциональность, я предполагаю разрешить ресурсам ссылаться на определение базового пространства имен в RDF хранилище или в файле. OwlClassAttribute наследует OwlResourceSupertype и может быть использован только с классами или структурами. Вы используете его (или базовый тип, если хотите) для того чтобы указать URI OWl класса, в виде которого будет сохраняться ваш тип. Так что для класса Track мы будем иметь соответствующий OWL класс "Track". В онтологии этот Track будет задаваться относительно некоторого базового URI, который я определяю, используя атрибут OntologyBaseUriAttribute. Этот атрибут задает URI онтологии относительно которого указываются URI классов и свойств. (например: "file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/").

Для свойств класса Track я определил другой подкласс OwlResourceSupertype - OwlPropertyAttribute, который применим только к свойствам. Еще одно упрощение по сравнению с OWL, которое я допускаю, заключается в том, что я не делаю различий между объектными свойствами (ObjectProperty) и свойствами данными (DatatypeProperty). Это будет не трудно добавить, и я уверен, что сделаю это в следующие несколько дней.

Так что теперь я обеспечил механизм сериализации аннотациями о том, как создавать из моего класса утверждения, которые я могу добавлять в RDF хранилище. Эти аннотации могут быть прочитаны механизмом сериализации и использованы для формирования подходящих URI для ресурсов. Мы все еще нуждаемся в инструменте для создания экземпляров объектов. Я решил эту проблему самым простым способом: я просто завел счетчик в сканере, и стал формировать URI экземпляра, добавляя значение счетчика к URI класса. Так что первый экземпляр будет иметь URI: "file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/Track_1", и т.д. Этот подход простой, но должен быть улучшен в любом серьезном приложении.

Далее мне нужно получить из экземпляра класса Track набор утверждений, который можно будет сохранить в RDF хранилище. Для этого я воспользовался новым средством C# 3.5 - методом расширения (extension method), который позволил мне писать такой код:

 
foreach (Track t in GetAllTracks(txtFrom.Text))
{
    t.InstanceUri = GenTrackName(t);
    store.Add(t);
}
 

Здесь store - это RDF хранилище, GetAllTracks - это итератор, который выдает файлы из директории, указанной в txtFrom.Text. GenTrackName - создает URI для экземпляров треков. Я бы мог использовать более изощренную схему с использованием хешей из местоположения треков, или что-нибудь еще, но я торопился ;-). Код механизма сериализации также не сложен:

 
public static class MemoryStoreExtensions
{
    public static void Add(this MemoryStore ms, OwlInstanceSupertype oc)
    {
        Debug.WriteLine(oc.ToString());
        Type t = oc.GetType();
        PropertyInfo[] pia = t.GetProperties();
        foreach (PropertyInfo pi in pia)
        {
            if(IsPersistentProperty(pi))
            {
                AddPropertyToStore(oc, pi, ms);
            }
        }
    }
    private static bool IsPersistentProperty(PropertyInfo pi)
    {
        return pi.GetCustomAttributes(typeof (OwlPropertyAttribute), true).Length > 0;
    }
    private static void AddPropertyToStore(OwlInstanceSupertype track, PropertyInfo pi, MemoryStore ms)
    {
        Add(track.InstanceUri, track.GetPropertyUri(pi.Name), pi.GetValue(track, null).ToString(), ms);
    }
    public static void Add(string s, string p, string o, MemoryStore ms)
    {
        if(!Empty(s) && !Empty(p) && !Empty(o))
        ms.Add(new Statement(new Entity(s), new Entity(p), new Literal(o)));
    }
    private static bool Empty(string s)
    {
        return (s == null || s.Length == 0);
    }
}
 

Add - метод расширения, который перебирает свойства класса OwlInstanceSupertype. OwlInstanceSupertype - базовый класс для всех классов, которые могут быть сохранены в хранилище. Как вы можете видеть, Add проверяет каждое свойство, является ли оно сохраняемым. И если да, то свойство сохраняется с помощью вызова AddPropertyToStore. AddPropertyToStore создает URI для субъекта (экземпляр трека в хранилище), предиката (объект свойство в классе Track) и объекта (который является строковым литералом содержащим значение свойства). Это утверждение добавляется в хранилище.

Вот и все. Почти. Небольшая онтология, которую я создал для музыкальных треков, выглядит следующим образом:

 
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix daml: <http://www.daml.org/2001/03/daml+oil#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .
@prefix xsdt: <http://www.w3.org/2001/XMLSchema#>.
@prefix : <http://aabs.purl.org/ontologies/2007/04/music#> .
 
:ProducerOfMusic a owl:Class.
:SellerOfMusic a owl:Class.
:NamedThing a owl:Class.
:TemporalThing a owl:Class.
:Person a owl:Class;
owl:subClassOf :NamedThing.
:Musician owl:subClassOf :ProducerOfMusic, :Person.
:Band a :ProducerOfMusic.
:Studio a :SellerOfMusic, :NamedThing.
:Label = :Studio.
:Music a owl:Class.
:Album a :NamedThing.
:Track a :NamedThing.
:Song a :NamedThing.
:Mp3File a owl:Class.
:Genre a :NamedThing.
:Style = :Genre.
:title
rdfs:domain :Track
rdfs:range xsdt:string.
:artistName
rdfs:domain :Track
rdfs:range xsdt:string.
:albumName
rdfs:domain :Track
rdfs:range xsdt:string.
:year
rdfs:domain :Album
rdfs:range xsdt:integer.
:genreName
rdfs:domain :Track
rdfs:range xsdt:string.
:comment
rdfs:domain :Track
rdfs:range xsdt:string.
:isTrackOn
rdfs:domain :Track
rdfs:range :Album.
:fileLocation
rdfs:domain :Track
rdfs:range xsdt:string.
 

Когда я запустил мой менеджер на директорию с подкастами, то получил на выходе следующий N3:

 
<file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/Track_1> 
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/title> "History 5 | Fall 2006 | UC Berkeley" ; 
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/artistName> "Thomas Laqueur" ;
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/albumName> "History 5 | Fall 2006 | UC Berkeley" ;
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/year> "2006" ;
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/genreName> "History 5 | Fall 2006 | UC Berkeley" ;
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/comment> " (C) Copyright 2006, UC Regents" ;
    <file:///C:/dev/prototypes/semantic-web/src/Mp3ToRdf/fileLocation> "C:\\Users\\andrew.matthews\\Music\\hist5_20060829.mp3" .
 

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

Перевод: Михаил Навернюк
Обновлено 10.04.2008 15:38
 
 
 
© 2010 Semantictools.ru. Все права защищены.
Joomla! — свободное программное обеспечение, распространяемое по лицензии GNU/GPL.
Design by augs-burg.de & go-vista.de
 
 
     
 
   
Design by windows vista forum and energiesparlampen