Как я могу проанализировать этот XML с атрибутами XmlSerializer? - PullRequest
0 голосов
/ 26 января 2012

Нам нужно использовать «веб-сервис», который передает крайне уродливый XML.Он не разработан нами, и нет никаких шансов заставить его разработчиков понять правильный XML.

К вашему сведению, этот веб-сервис также принимает тот же тип XML в параметре HTTP GET URL (не в запросе).body) - и ребята, которые его разработали, не понимают, почему это плохая практика.

Итак, какой самый быстрый способ отобразить XML, такой как:

<foo id="document">
    <foo id="customer">
        <bar name="firstname" value="Joe"/>
        <bar name="lastname" value="Smith"/>
        <foo id="address">
            <bar name="city" value="New York"/>
            <bar name="country" value="USA"/>
        </foo>
    </foo>
    <bar name="somemoredata1" value="123"/>
    <bar name="somemoredata2" value="abc"/>
</foo>

в такие классы:

public class Document
{
    public Customer Customer { get; set; }

    public int SomeMoreData1 { get; set; }

    public string SomeMoreData2 { get; set; }
}

public class Customer
{
    public Address Address { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }
}


public class Address
{
    public string City { get; set; }

    public string Country { get; set; }
}

используя например.Атрибуты XML Serializer или любой другой способ, который требует как можно меньшего количества стандартного кода.

Я составил имена элементов foo и bar, но структура XML, который мне нужно проанализировать, основана наточно такое же соглашение.

Конечно, я мог бы реализовать IXmlSerializable вручную в этих классах или просто создать Foo и Bar классы и использовать их с XmlSerializer, но ни одна из этих опций, похоже, не подходитхорошее решение.

Заранее спасибо за ответы!

Ответы [ 4 ]

1 голос
/ 27 января 2012

Поскольку вы говорите в комментарии, что вы хотите использовать XmlSerializer для простоты, а не потому, что этот подход вызван другими проблемами, здесь есть другой подход.Поскольку кажется, что имена элементов в документе незначительны, я игнорирую его при разборе, хотя это тоже можно проверить.С более приятными форматами XML это было бы главным, что бы анализ отключался (обычно с switch в именах элементов):

private static Document ParseDocument(XmlReader xr)
{
    Document doc = new Document();
    while(xr.Read())
      if(xr.NodeType == XmlNodeType.Element)
        if(xr.GetAttribute("id") == "customer")
          doc.Customer = ParseCustomer(xr.ReadSubtree());
        else
          switch(xr.GetAttribute("name"))
          {
            case "somemoredata1":
              doc.SomeMoreData1 = int.Parse(xr.GetAttribute("value"));
              break;
            case "somemoredata2":
              doc.SomeMoreData2 = xr.GetAttribute("value");
              break;
          }
      //Put some validation of doc here if necessary.
      return doc;
}
private static Customer ParseCustomer(XmlReader xr)
{
  Customer cu = new Customer();
  while(xr.Read())
    if(xr.NodeType == XmlNodeType.Element)
      if(xr.GetAttribute("id") == "address")
        cu.Address = ParseAddress(xr.ReadSubtree());
      else
        switch(xr.GetAttribute("name"))
        {
          case "firstname":
            cu.FirstName = xr.GetAttribute("value");
            break;
          case "lastname":
            cu.LastName = xr.GetAttribute("value");
            break;
        }
    //validate here if necessary.
    return cu;
}
private static Address ParseAddress(XmlReader xr)
{
  Address add = new Address();
  while(xr.Read())
    if(xr.NodeType == XmlNodeType.Element)
      switch(xr.GetAttribute("name"))
      {
        case "city":
          add.City = xr.GetAttribute("value");
          break;
        case "country":
          add.Country = xr.GetAttribute("value");
          break;
      }
  return add;
}

Это не совсем красиво (это не очень красиво)с хорошим XML, чтобы работать, но это, как правило, не так уж плохо), но это работает, и использование поддеревьев может быть хорошо с некоторыми сложными структурами, где один и тот же тип может появляться в разных местах в документе.Можно заменить статические методы, которые устанавливают значения извне, на конструкторы, которые принимают XmlReader, что позволяет гарантировать инварианты классов и / или иметь неизменяемые объекты.

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

1 голос
/ 26 января 2012

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

<xsl:template match="foo">
  <xsl:element name="{@id}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="bar">
  <xsl:element name="{@name}">
    <xsl:value-of select="@value"/>
  </xsl:element>
</xsl:template>

Обновление: для обратного преобразования:

<xsl:template match="*[count(child::text())=1]">
  <bar value="{text()}" name="{local-name()}"/>
</xsl:template>

<xsl:template match="*">
  <foo id="{local-name()}">
    <xsl:apply-templates/>
  </foo>
</xsl:template>
1 голос
/ 26 января 2012

Я сам не использовал сериализатор или десериализатор xml, но я использую LINQ для разбора моего документа XML на объекты. Если ваши занятия довольно просты, вы можете посмотреть на этот маршрут.

0 голосов
/ 26 января 2012

Вы можете попробовать "Инструмент определения схемы XML" (http://msdn.microsoft.com/de-de/library/x6c1kb0s%28v=vs.80%29.aspx)

Ciao! Stefan

...