Синтаксический анализ XML в C # с помощью XmlSeralizer - PullRequest
2 голосов
/ 25 августа 2009

Мне дали несколько файлов "XML", которые не имеют надлежащей схемы (я думаю, что это проблема), и медицинское устройство, которое их генерирует, не может быть изменено для создания простого для анализа XML. (С такой невероятно маленькой модификацией (дополнительная упаковка Images тегов вокруг Image записей) было бы тривиально читать эти файлы - разве это не то, о чем XML?)

По сути, я застрял здесь. XML выглядит так:

<Series>
   <Metadata1>foo</Metadata1>
   <Metadata2>bar</Metadata2>
   ...
   <Image>...</Image>
   <Image>...</Image>
   ...
</Series>

(может быть любое количество изображений, но все возможные теги метаданных известны). Мой код выглядит так:

public class Image { ... }

public class Series : List<Image>
{
    public Series() { }
    public string Metadata1;
    public string Metadata2;
    ...
}

Когда я запускаю это так:

            XmlSerializer xs = new XmlSerializer(typeof(Series));
            StreamReader sr = new StreamReader(path);
            Series series = (Series)xs.Deserialize(sr);
            sr.Close();

объекты List of Image правильно читают данные в объекте серии, но поля Metadata1 / 2 / etc не читаются (фактически, при просмотре объекта в отладчике отображаются все поля метаданных внутри поля вида «Raw View») ).

Когда я меняю код:

public class Series    // // removed this : List<Image>
{
    public Series() { }
    public string Metadata1;
    public string Metadata2;
    ...
}

и запустив ридер по файлу, я получаю объект серии с Metadata1 / 2 / etc. заполнены идеально, но данные изображения не читаются (очевидно).

Как мне проанализировать обе метаданные1 / 2 / и т.д. и серия изображений с наименьшим количеством болезненного специального кода?

Должен ли я написать какой-нибудь собственный (болезненный? Легкий?) Метод ReadXML для реализации IXMLSeralizable?

Мне не особо важно, как расположены объекты, так как мое программное обеспечение, которое использует эти классы C #, абсолютно гибкое:

List<Image> Images;
для изображений было бы хорошо, или, возможно, метаданные обернуты в некоторый объект, что угодно. ..

Ответы [ 3 ]

3 голосов
/ 25 августа 2009

В ваших классах отсутствуют атрибуты , которые позволяют сериализацию XML работать. Я считаю, что следующего должно быть достаточно.

[XmlElement]
public class Image { ... }

[XmlRoot(ElementName="Series")]
public class Series
{
        public Series() { }

        [XmlElement]
        public string Metadata1;

        [XmlElement]
        public string Metadata2;

        [XmlElement(ElementName="Image")]
        public Image[] Images;
}

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

РЕДАКТИРОВАТЬ: Другой вариант заключается в создании вручную и XML-схемы, которая будет проверять документы, созданные приложением, а затем использовать XSD.exe для создания объектной модели. Получающиеся классы продемонстрируют, как вы должны настроить свою объектную модель для работы с сериализатором.

2 голосов
/ 25 августа 2009

Почему вы пытаетесь использовать для этого сериализатор XML? Сериализация, как правило, заключается в возможности сохранить «состояние» объекта в каком-то общеизвестном формате (текстовом или двоичном), чтобы его можно было воссоздать позднее. Это не похоже на то, что вы пытаетесь сделать здесь. Проблема в том, что данные XML не соответствуют вашей иерархии объектов.

У вас есть аппаратное устройство, которое каким-то образом генерирует данные XML, которые вы хотите использовать. Для меня это было бы проще, если бы использовать простой класс XmlDocument или XmlReader, а не пытаться пройти через сериализатор.

Возможно, вы могли бы сделать это с помощью кода, подобного следующему:

public class Image { }

public class Series
{
   public string Metadata1;
   public string Metadata2;
   public List<Image> Images = new List<Image>();

   public void Load(string xml)
   {
      XmlDocument doc = new XmlDocument();
      doc.Load(xml);

      XmlNodeList images = doc.SelectNodes("Image");
      foreach (XmlNode image in images)
      {
         Images.Add(new Image(image.InnerText));
      }

      Metadata1 = GetMetadataValue(doc, "Metadata1");
      Metadata2 = GetMetadataValue(doc, "Metadata2");
   }

   private string GetMetadataValue(XmlDocument document, string nodeName)
   {
      string value = String.Empty;
      XmlNode metadataNode = document.SelectSingleNode(nodeName);
      if (metadataNode != null)
      {
         value = metaDataNode.InnerText;
      }

      return value;
   }
}

* Это непроверенный / непроверенный код, но он должен донести идею.

1 голос
/ 25 августа 2009

Я думаю, что ответ Стива должен сработать. Я просто хочу добавить, что с помощью этой техники вы можете читать только конечное количество элементов метаданных, потому что они не имеют постоянного имени. Что вы могли бы сделать, так это прочитать их в коллекцию элементов XmlElements, которые вы можете проанализировать позже:

[XmlRoot(ElementName="Series")]
public class Series
{
    public Series() { }

    [XmlAnyElement]
    XmlElement[] UnknownElements;

    private string[] _metadata;
    [XmlIgnore]
    public string[] Metadata
    {
        get
        {
            if (_metadata == null && UnknownElements != null)
            {
                _metadata = UnknownElements
                            .Where(e => e.Name.StartsWith("Metadata")
                            .Select(e => e.InnerText)
                            .ToArray();
            }
            return _metadata;
        }
    }

    [XmlElement(ElementName="Image")]
    public Image[] Images;
}
...