【问题标题】:What is wrong with my data contract for this Xml deserialization?我的这个 Xml 反序列化的数据合同有什么问题?
【发布时间】:2018-01-19 10:30:39
【问题描述】:

我正在努力了解 xml 反序列化的细节。虽然我已将要传递的字符串传递给将 Xml 转换为 C# 数据协定类表示法的在线解析器,但我似乎看不到我的 Xml 或类对象代码中是什么导致了此错误。我确信它是一些小而有语法意义的东西。

引发以下异常:

InvalidOperationException: 不是预期的。

执行代码时:

string xml =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
    "<folder name=\"c\">" +
        "<folder name=\"program files\">" +
            "<folder name=\"uninstall information\" />" +
        "</folder>" +
        "<folder name=\"users\" />" +
    "</folder>";

foreach (string name in Folders.FolderNames(xml, 'u'))
    Console.WriteLine(name);

控制台的预期输出(以任何顺序):

卸载信息

用户

支持类代码:

[XmlRoot(ElementName = "folder")]
public class Folder
{
    [XmlAttribute(AttributeName = "name")]
    public string Name { get; set; }
}

public class Folders
{        
    public static IEnumerable<string> FolderNames(string xml, char startingLetter)
    {
        List<Folder> folders = null;
        XmlSerializer serializer = new XmlSerializer(typeof(List<Folder>));
        StringReader reader = new StringReader(xml);
        folders = (List<Folder>)serializer.Deserialize(reader);

        List<string> result = new List<string>();
        foreach (Folder folder in folders)
        {
            if (folder.Name.StartsWith(startingLetter.ToString()))
            {
                result.Add(folder.Name);
            }
        }
        reader.Close();
        return result;
    }
}

【问题讨论】:

    标签: c# xml deserialization


    【解决方案1】:

    在下面查看我当前的答案代码。这是一项正在进行的工作,因为 Testdome 说它是 50% 正确的。当前答案代码通过了 Testdome 的 4 个单元测试中的 2 个。这让我感到困惑,因为它通过了我所有的单元测试。

    Testdome 单元测试结果:

    • 示例案例:正确答案

    • 所有文件夹名称以首字母开头:正确答案

    • 根文件夹名称以首字母开头:错误答案*

    • 复杂的文件夹结构:错误答案*

    *上面的错误答案表明:“您的代码为测试用例返回了错误的答案。创建您自己的测试用例以找出代码出错的地方。”

    编辑

    我还请求了“显示提示”,Testdome 引擎显示了这个:

    提示 1:XDocument 提供了一种基于 LINQ 的便捷方法来解析 XML。

    这很有趣,因为它会完全改变我的答案。有了这个,我不相信递归函数是必需的。使用 LINQ,可以只从反序列化的 Xml 构建结果列表。我会努力解决这个问题并将我的发现发回......

    我的第一个答案代码(使用递归——错误答案——继续阅读):

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml.Serialization;
    
    [XmlRoot(ElementName = "folder")]
    public class Folder
    {
        [XmlAttribute(AttributeName = "name")]
        public string Name { get; set; }
    
        [XmlElement(ElementName = "folder")]
        public List<Folder> children { get; set; }
    }
    
    public class Folders
    {
        public static IEnumerable<string> FolderNames(string xml, char startingLetter)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Folder));
            StringReader reader = new StringReader(xml);
            Folder folders = (Folder)serializer.Deserialize(reader);
    
            searchFolders(folders, startingLetter);
    
            reader.Close();
            return searchFolderResults;
        }
    
        private static List<string> searchFolderResults = new List<string>();
    
        private static void searchFolders(Folder node, char startingLetter)
        {
            if (node.Name.StartsWith(startingLetter.ToString()))
            {
                searchFolderResults.Add(node.Name);
            }
            foreach (Folder folder in node.children)
                searchFolders(folder, startingLetter);           
        }
    
        public static void Main(string[] args)
        {
            string xml =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<folder name=\"c\">" +
                "<folder name=\"program files\">" +
                    "<folder name=\"uninstall information\" />" +
                    "<folder name=\"cusers3\" />" +
                "</folder>" +
                "<folder name=\"users\">" +
                    "<folder name=\"users2\" />" +
                "</folder>" +
            "</folder>";
    
            foreach (string name in Folders.FolderNames(xml, 'c'))
                Console.WriteLine(name);
        }
    }
    

    编辑

    我的最新代码(使用 XDocument 和 LINQ,并通过了另一个单元测试):

    Testdome 单元测试结果:

    • 示例案例:正确答案

    • 所有文件夹名称以首字母开头:正确答案

    • 根文件夹名称以首字母开头:正确答案

    • 复杂的文件夹结构:错误答案*

    *上面的错误答案表明:“您的代码为测试用例返回了错误的答案。创建您自己的测试用例以找出代码出错的地方。”

    我再次点击“显示提示”,现在它有第二个提示:

    提示 2:可能还需要考虑文档的根节点。

    所以我再次离开继续工作,直到解决所有 Testdome 单元测试。他们声称这是一个困难的 C# 问题。我想……

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Serialization;
    
    
    [XmlRoot(ElementName = "folder")]
    public class Folder
    {
        [XmlAttribute(AttributeName = "name")]
        public string Name { get; set; }
    
        [XmlElement(ElementName = "folder")]
        public List<Folder> children { get; set; }
    }
    
    public class Folders
    {
        public static IEnumerable<string> FolderNames(string xml, char startingLetter)
        {
            return
                from row in XDocument.Parse(xml).Descendants()
                where row.FirstAttribute.Value.StartsWith(startingLetter.ToString())
                select row.FirstAttribute.Value;
        }
    
        public static void Main(string[] args)
        {
            string xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<folder name=\"c\">" +
                    "<folder name=\"program files\">" +
                        "<folder name=\"uninstall information\" />" +
                        "<folder name=\"cusers3\" />" +
                    "</folder>" +
                    "<folder name=\"users\">" +
                        "<folder name=\"users2\" />" +
                    "</folder>" +
                "</folder>";
    
            foreach (string name in Folders.FolderNames(xml, 'c'))
                Console.WriteLine(name);
        }
    }
    

    最后编辑(我保证)

    我在上面的最新答案(涉及 XDocument 和 LINQ)的问题是我使用了 Contains 而不是 StartsWith,并且在搜索“u”时未过滤“cusers3”的复杂测试中失败了”。更正此逻辑后,它在 Testdome 的测试引擎中完美运行。谢谢!!

    顺便说一句..第二个提示(见上文)是red herring(不是可疑的那种)。

    【讨论】:

      【解决方案2】:

      尝试以下已经过测试的

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Xml;
      using System.Xml.Serialization;
      using System.IO;
      
      namespace ConsoleApplication1
      {
          class Program
          {
              static void Main(string[] args)
              {
                  string xml =
                      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                      "<folders name=\"c\">" +
                          "<folder name=\"program files\">" +
                              "<folder name=\"uninstall information\" />" +
                          "</folder>" +
                          "<folder name=\"users\" />" +
                      "</folders>";
      
                  List<string> folders =  Folders.FolderNames(xml, 'u').ToList();
              }
          }
          [XmlRoot(ElementName = "folder")]
          public class Folder
          {
              [XmlAttribute(AttributeName = "name")]
              public string Name { get; set; }
          }
      
          [XmlRoot(ElementName = "folders")]
          public class Folders
          {
              [XmlElement("folder")]
              public List<Folder> folders { get; set; }
      
              public static IEnumerable<string> FolderNames(string xml, char startingLetter)
              {
                  XmlSerializer serializer = new XmlSerializer(typeof(Folders));
                  StringReader reader = new StringReader(xml);
                  Folders folders = (Folders)serializer.Deserialize(reader);
      
                  List<string> result = new List<string>();
                  foreach (Folder folder in folders.folders)
                  {
                      if (folder.Name.StartsWith(startingLetter.ToString()))
                      {
                          result.Add(folder.Name);
                      }
                  }
                  reader.Close();
                  return result;
              }
          }
      }
      

      【讨论】:

      • 谢谢jdweng。我检查了您的答案,虽然它确实编译并输出了结果,但结果不完整。它输出“用户”,还应该输出“卸载信息”。我认为这更接近。我注意到您将 xml 中的节点名称从“文件夹”更改为“文件夹”,并且想知道这是否偏离了解决方案的轨道。我相信这里需要一些递归,其中“文件夹”可能包含名称为“文件夹”的其他节点,依此类推。因此,通过递归,可以遍历“文件夹”的嵌套节点。谢谢。我还在研究这个问题...
      • Serialize 方法不会进行递归。而且您的类结构不包含子节点。对于递归 xml,请参阅以下帖子:stackoverflow.com/questions/28976601/…
      • 看我的回答。可以使用 Xml 结构进行递归,尽管我的代码没有通过 Testdome 的单元测试引擎检查。即使它通过了我所有的测试。我运行了很多测试,这个东西输出的正是预期的,但 Testdome 说它失败了。我将使用 Testdome 单元测试结果发布对我的答案的编辑..
      【解决方案3】:

      使用System.Xml.LinqSystem.Linq; 的另一种解决方案

      using System;
      using System.Collections.Generic;
      using System.Xml.Linq;
      using System.Linq;
      
      namespace FoldersApp
      {
      
          public class Folders
          {
      
              public static IEnumerable<string> FolderNames(string xml, char startingLetter)
              {
                  return XDocument.Parse(xml).Descendants()
                       .Where(x =>
                       x.Attribute("name") != null &&
                       x.Attribute("name").Value.StartsWith(Char.ToString(startingLetter)))
                       .Select(x => x.Attribute("name").Value);
              }
      
              public static void Main(string[] args)
              {
                  string xml =
                      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                      "<folder name=\"c\">" +
                          "<folder name=\"program files\">" +
                              "<folder name=\"uninstall information\" />" +
                          "</folder>" +
                          "<folder name=\"users\" />" +
                      "</folder>";
      
                  foreach (string name in Folders.FolderNames(xml, 'u'))
                  Console.WriteLine(name);
      
              }
          }
      
      
      }
      

      【讨论】:

        【解决方案4】:

        这里是完整的解决方案

        using System;
        using System.Collections.Generic;
        using System.Xml;
        
        namespace Folders
        {
            class Program
            {
                public static List<string> FolderNames(string xml, char startingLetter)
                {
                    var list = new List<string>();
                    var doc = new XmlDocument();
                    doc.LoadXml(xml);
        
                    FindNodes(list, doc.ChildNodes, startingLetter);
        
                    return list;
                }
        
                private static void FindNodes(List<string> results, XmlNodeList nodes, char startingLetter)
                {
                    foreach (XmlNode item in nodes)
                    {
                        if (item.Attributes == null)
                            continue;
        
                        if (item.Attributes["name"] == null)
                            continue;
        
                        var folder = item.Attributes["name"];
                        if (folder.Value.ToLower().StartsWith(startingLetter.ToString()))
                        {
                            results.Add(folder.Value);
                        }
        
                        if (!item.HasChildNodes)
                            continue;
        
                        FindNodes(results, item.ChildNodes, startingLetter);
                    }
                }
        
                public static void Main(string[] args)
                {
                    string xml =
                        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                        "<folder name=\"c\">" +
                            "<folder name=\"program files\">" +
                                "<folder name=\"uninstall information\" />" +
                            "</folder>" +
                            "<folder name=\"users\" />" +
                        "</folder>";
        
                    foreach (string name in FolderNames(xml, 'u'))
                        Console.WriteLine(name);
        
                    Console.ReadKey();
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2017-10-23
          • 1970-01-01
          • 1970-01-01
          • 2012-05-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-09
          • 1970-01-01
          相关资源
          最近更新 更多