Аннотации XML

92

API-интерфейс LINQ to XML предоставляет возможность ассоциировать объект пользовательских данных с любым классом-наследником XObject через аннотации. Это позволяет разработчику приложений назначать данные любого типа элементу, документу или любому другому объекту, чей класс наследуется от XObject. Объект может быть дополнительными ключами для данных элемента, объектом, который разберет содержимое элемента и сохранит его в себе в ином виде, либо чем-то другим.

Добавление аннотаций с помощью XObject.AddAnnotation()

Добавление аннотаций осуществляется с помощью метода XObject.AddAnnotation. Вот его прототип:

void XObject.AddAnnotation(object annotation);
Обращение к аннотациям с помощью XObject.Annotation() или XObject.Annotations()

Обращение к аннотациям осуществляется с использованием методов XObject.Annotation или XObject.Annotations. Ниже показаны их прототипы:

object XObject.Annotation(Type type); 
T XObject.Annotation<T>();
IEnumerable<object> XObject.Annotations(Type type); 
IEnumerable<T> XObject.Annotations<T>();

При извлечении аннотаций должен передаваться действительный тип объекта, а не базовый класс или интерфейс. В противном случае аннотация не будет найдена.

Удаление аннотаций с помощью XObject.RemoveAnnotations()

Удаление аннотаций осуществляется методом XObject.RemoveAnnotations. У него есть два прототипа:

void XObject.RemoveAnnotations(Type type); 
void XObject.RemoveAnnotations<T>();
Пример аннотаций

Для демонстрации аннотаций создадим один пример, в котором аннотации будут добавляться, извлекаться и удаляться. В этом примере используется привычное XML-дерево Employees (Сотрудники). Необходим способ ассоциирования обработчика каждого Employee на основе его атрибута type. В данном примере обработчик будет просто отображать элемент в специфичном для атрибута type формате: один формат для авторов и другой — для редакторов.

Сначала понадобится пара классов — по одному для программистов и редакторов:

public class ProgrammerHandler
  {
    public void Display(XElement element)
    {
      Console.WriteLine("БИОГРАФИЯ ПРОГРАММИСТА");
      Console.WriteLine("--------------------------");
      Console.WriteLine("Name:        {0} {1}",
        (string)element.Element("FirstName"),
        (string)element.Element("LastName"));
      Console.WriteLine("Language:    {0}", (string)element.Attribute("language"));
      Console.WriteLine("Experience:  {0}", (string)element.Attribute("experience"));
      Console.WriteLine("==========================" + System.Environment.NewLine);
    }
  }

  public class EditorHandler
  {
    public void Display(XElement element)
    {
      Console.WriteLine("БИОГРАФИЯ РЕДАКТОРА");
      Console.WriteLine("--------------------------");
      Console.WriteLine("Name:        {0}", (string)element.Element("FirstName"));
      Console.WriteLine("             {0}", (string)element.Element("LastName"));
      Console.WriteLine("==========================" + System.Environment.NewLine);
    }
  }

Здесь нет ничего особенного. Нужны два обработчика, ведущих себя по-разному. В этом случае они будут отображать данные элемента в немного отличающемся формате. Конечно, они не обязательно должны только отображать данные. Они могут делать все, что необходимо. Аннотации также могут не быть обработчиками, а просто какими-то ассоциированными данными. Но в рассматриваемом примере это обработчики.

Поскольку этот пример сложнее, чем обычно, части кода разделяются пояснениями, как показано ниже:

XElement firstEmployee;

      XDocument xDocument = new XDocument(
       new XElement("Employees", firstEmployee =
         new XElement("Employee",
           new XAttribute("type", "Programmer"),
           new XAttribute("experience", "first-time"),
           new XAttribute("language", "Russian"),
           new XElement("FirstName", "Alex"),
           new XElement("LastName", "Erohin")),
         new XElement("Employee",
           new XAttribute("type", "Editor"),
           new XElement("FirstName", "Elena"),
           new XElement("LastName", "Volkova"))));

      Console.WriteLine("{0}{1}", xDocument, System.Environment.NewLine);

Все, что делается в этой части — это построение типичного документа XML, который будет использован далее, а также его отображение. В следующей части кода выполняется перечисление сотрудников, и для каждого создается экземпляр обработчика на основе значения атрибута type. Затем к элементу добавляется аннотация для соответствующего обработчика:

//  Добавить некоторые аннотации на основе значения атрибута type
      foreach (XElement e in xDocument.Element("Employees").Elements())
      {
        if ((string)e.Attribute("type") == "Programmer")
        {
          ProgrammerHandler aHandler = new ProgrammerHandler();
          e.AddAnnotation(aHandler);
        }
        else if ((string)e.Attribute("type") == "Editor")
        {
          EditorHandler eHandler = new EditorHandler();
          e.AddAnnotation(eHandler);
        }
      }

Теперь каждый элемент Employee имеет добавленный к нему обработчик в виде аннотации, зависящий от его атрибута type. Теперь можно выполнить перечисление элементов с вызовом обработчика, извлеченного из аннотации элемента:

ProgrammerHandler aHandler2;
      EditorHandler eHandler2;
      foreach (XElement e in xDocument.Element("Employees").Elements())
      {
        if ((string)e.Attribute("type") == "Programmer")
        {
          aHandler2 = e.Annotation<ProgrammerHandler>();
          if (aHandler2 != null)
          {
            aHandler2.Display(e);
          }
        }
        else if ((string)e.Attribute("type") == "Editor")
        {
          eHandler2 = e.Annotation<EditorHandler>();
          if (eHandler2 != null)
          {
            eHandler2.Display(e);
          }
        }
      }

В этой точке для каждого элемента будет вызван обработчик отображения. Конкретный вызываемый обработчик зависит от атрибута type. Затем аннотации элементов удаляются:

foreach (XElement e in xDocument.Element("Employees").Elements())
      {
        if ((string)e.Attribute("type") == "Programmer")
        {
          e.RemoveAnnotations<ProgrammerHandler>();
        }
        else if ((string)e.Attribute("type") == "Editor")
        {
          e.RemoveAnnotations<EditorHandler>();
        }
      }

Этот пример состоит из четырех основных разделов. В первом разделе строится и отображается документ XML. Это уже много раз демонстрировалось. Во втором разделе выполняется перечисление элементов Employees, с добавлением к каждому обработчика на основе атрибута type. В третьем разделе вновь производится перечисление элементов Employees, при этом на основе атрибута type извлекается обработчик и вызывается метод Display объекта обработчика. В четвертом разделе элементы Employees опять перечисляются с целью удаления аннотаций.

Обратите внимание, что при обращении к атрибутам они приводятся к string, чтобы получить значение.

Следует помнить, что эти аннотации могут быть любыми объектами данных, которые нужно ассоциировать с элементом. И, наконец, вот результат:

Добавление, извлечение и удаление аннотаций

Относительно результатов также важно отметить, что разные обработчики вызываются в зависимости от атрибута type с использованием аннотаций. Естественно, объекты, которые добавляются в виде аннотаций, могут служить любой цели, а не обязательно быть какими-то обработчиками.

Пройди тесты
Лучший чат для C# программистов