C# 如何使用XmlSerializer属性解析此XML?

C# 如何使用XmlSerializer属性解析此XML?,c#,.net,xml-serialization,xmlserializer,C#,.net,Xml Serialization,Xmlserializer,我们需要使用一个web服务来传递极其丑陋的XML。它不是由我们开发的,并且没有机会让它的开发人员理解正确的XML 仅供参考,此web服务还接受HTTP GET URL参数中的同一种XML,而不是请求主体-开发它的人不明白为什么这是一种不好的做法 那么,映射如下XML的最快方法是什么: <foo id="document"> <foo id="customer"> <bar name="firstname" value="Joe"/>

我们需要使用一个web服务来传递极其丑陋的XML。它不是由我们开发的,并且没有机会让它的开发人员理解正确的XML

仅供参考,此web服务还接受HTTP GET URL参数中的同一种XML,而不是请求主体-开发它的人不明白为什么这是一种不好的做法

那么,映射如下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>
使用XML序列化程序属性或任何其他需要尽可能少的样板代码的方式

我编写了foo和bar元素名称,但是我需要解析的XML的结构基于完全相同的约定

当然,我可以在这些类中手动实现IXmlSerializable,或者只创建Foo和Bar类,并将它们与XmlSerializer一起使用,但这些选项似乎都不是一个好的解决方案


提前感谢您的回答

我自己没有使用xml序列化程序或反序列化程序,但我确实使用LINQ将xml文档解析为对象。如果你的课程相当简单,你可以研究这条路线

您可以尝试使用XML模式定义工具

再见!
Stefan

您不能使用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>

由于您在评论中说,为了简单起见,而不是因为其他关注点强制使用了XmlSerializer,所以您选择了另一种方法。因为元素的名称在文档中似乎无关紧要,所以我在解析时忽略了它,尽管人们也可以测试它。对于更令人愉快的XML格式,这将是解析通常在打开元素名称时关闭的主要内容:

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并不十分漂亮,但它往往没有那么糟糕,但它是有效的,子树的使用可以很好地处理一些复杂的结构,其中相同的类型可以出现在文档中的不同位置。可以用contstructor替换从外部设置值的静态方法,contstructor接受XmlReader,从而确保类不变量和/或使对象不可变


这种方法适用于需要反序列化为一大系列相同类型的项或一大系列只有几种类型的大型文档,因为创建时可以生成它们,这会对第一次响应的延迟产生很大影响。

是否需要使用XmlSerialiser?我经常发现,即使使用好的XML,用XmlReader解析和构建也很容易,更不用说疯狂的XML了。如果您有更好或更简单的解决方案,请写一个答案!:我对此很好奇。+1推荐LINQ到XML;但是,如果上面粘贴的XML代表了XML OP正在接收的内容,那么它可能会被证明是具有挑战性的,因为它看起来没有良好的格式,例如没有根节点。我之所以考虑使用XmlSerializer,是因为它使我不必编写大量样板代码。Jon。我几乎否决了你在这种情况下的用法。XmlReader已传递给该方法,调用方可能仍然需要它。@JohnSaunders要做什么?OP中的文档将在完成时被读取到最后,因此XmlReader唯一要做的就是处置它。如果他们想要倒带,那么他们需要以某种方式存储它,比如string、XmlDocument等,这样他们仍然可以以任何一种方式重复使用,并且XmlReader来自ReadSubtree,那么对Dispose的调用不会阻止进一步读取父读取器。诚然,我可能会有更高层次的使用,但它应该在某个地方得到完全的使用,所以我把它放在这里是为了表示良好的实践。处理传递给方法的资源不是最佳实践。Jon,这种代码编写和维护起来很痛苦。这正是我喜欢序列化程序的原因。@JohnSaunders确实,您不会处理使用CommandBehavior.CloseConnection创建并传递给迭代器方法中迭代器方法的IDataReader?哈,您的XSLT思想是迄今为止最优雅的!还有一件事:如何将序列化程序输出的XML转换回丑陋的格式?我这么问是因为我对XSLT有点不熟悉。为反向转换添加了XSLT,并修复了另一个。谢谢Anton!XSLT节省了时间:
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;
}