使用 XmlSeralizer 在 C# 中解析稍有偏差的 XML

Posted

技术标签:

【中文标题】使用 XmlSeralizer 在 C# 中解析稍有偏差的 XML【英文标题】:Parsing slightly off-kilter XML in C# with XmlSeralizer 【发布时间】:2009-08-25 00:16:57 【问题描述】:

我收到了一些“XML”文件,它们的架构并不完全正确(我认为这是问题所在),并且无法更改生成它们的医疗设备以生成易于解析的 XML。 (通过如此诱人的小修改(在 Image 条目周围额外包装 Images 标记)读取这些文件将是微不足道的——这不是 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();

图像对象列表正确读入系列对象,但没有读取 Metadata1/2/etc 字段(事实上,在调试器中浏览对象会显示“原始视图”类字段内的所有元数据字段)。

当我更改代码时:

public class Series    // // removed this : List<Image>

    public Series()  
    public string Metadata1;
    public string Metadata2;
    ...

然后在文件上运行阅读器,我得到一个带有 Metadata1/2/etc 的系列对象。完美填充但没有读取图像数据(显然)。

如何同时解析 Metadata1/2/etc.以及痛苦的临时代码最少的一系列图像?

我是否必须编写一些自定义(痛苦?容易?)ReadXML 方法来实现 IXMLSeralizable?

我不太关心对象的布局,因为我使用这些 C# 类的软件非常灵活:

List Images;
图像会很好,或者元数据可能包含在某个对象中,无论如何......

【问题讨论】:

【参考方案1】:

您的类缺少 the attributes 允许 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 生成对象模型。生成的类将演示您应该如何调整对象模型以使用序列化程序。

【讨论】:

我需要的唯一部分是:[XmlElement(ElementName="Image")] public Image[] Images;它工作得很好。 public class Image ... 上的 [XmlElement] 属性是不必要的(并且确实不编译:“属性 'XmlElement' 在此声明类型上无效。它在 'property, indexer, field, param, return ' 仅限声明。”【参考方案2】:

您为什么要尝试使用 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;
   

*这是未经测试/未经验证的代码,但它应该可以理解。

【讨论】:

哇,这会将 160 行代码变成 1600 行代码。我想问题是 XmlSerializer 让我很接近,只是不是一直到那里。您发布的内容与 XMLSerializer 兼容吗?这是一组更大的 16 个类的一部分,否则这些类可以使用 XmlSerializer 正确解析。【参考方案3】:

我认为史蒂夫的回答应该有效。我只想补充一点,您只能使用这种技术读取有限数量的元数据元素,因为它们没有常量名称。你可以做的是将它们读入一个 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;

【讨论】:

我过度简化了我的 XML 示例,并引入了对 Metadata* 的非要求。我应该只保留我试图解析的原始类/字段!对于那个很抱歉。我的意思是 Metadata1/2/等。只是我能够正确解析的字段和对象的集合(Foo,Bar,Bat,Baz,你的名字)。数字没有任何意义,问题的要点(对不起,如果我没有传达它)是如何在 Series 对象中解析那些“正常”字段/对象(我称之为 Metadata1,Metadata2)并解析Image 对象序列到 List 中。聪明的解决方案!

以上是关于使用 XmlSeralizer 在 C# 中解析稍有偏差的 XML的主要内容,如果未能解决你的问题,请参考以下文章

c#中 如何用List<string>作为Dictionary的key。

在 C# 中使用正则表达式解析电子邮件

使用 C# 与 PHP 的 AES GCM 加密

在 C# 中使用 LINQ 解析 XML

如何在 C# 中使用 CSV 帮助器解析 TSV 文件?

如何在 C# 中使用正则表达式解析重复的名称-值对