XML linq 需要有关异常的详细信息

Posted

技术标签:

【中文标题】XML linq 需要有关异常的详细信息【英文标题】:XML linq need detail info on exception 【发布时间】:2014-11-08 10:27:10 【问题描述】:

我在我的项目中使用 xml linq。为了便于理解,我正在处理非常大的 xml,我已经提到了小样本 xml。

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
    <***Reply xmlns="http://xml.stack.com/RRAND01234">
        <processStatus>
            <statusCode1>P</statusCode1>
            <statusCode2>P</statusCode2>
            <statusCode3>P</statusCode3>
            <statusCode4>P</statusCode4>
        </processStatus>
    </***Reply>
</soap:Body>

以下是 C# xml linq

XNamespace x = "http://xml.stack.com/RRAND01234";
var result = from ***Reply in XDocument.Parse(Myxml).Descendants(x + "Security_AuthenticateReply")
             select new
             
                status1 = ***Reply.Element(x + "processStatus").Element(x + "statusCode1").Value,
                status2 = ***Reply.Element(x + "processStatus").Element(x + "statusCode2").Value,
                status3 = ***Reply.Element(x + "processStatus").Element(x + "statusCode3").Value,
                status4 = ***Reply.Element(x + "processStatus").Element(x + "statusCode4").Value,
                status5 = ***Reply.Element(x + "processStatus").Element(x + "statusCode5").Value,
              ;

在这里,我遇到了“对象引用未设置为对象的实例”之类的异常。因为标签

<statusCode5>

不在我的 xml 中。在这种情况下,我想获取详细的异常消息,例如“缺少标记 statusCode5”。请指导我如何从我的异常中获取此消息。

【问题讨论】:

如果你想要一个详细的异常,你必须首先检查元素是否存在或尝试将所有statusCode节点一起提取。你尝试过做什么? 如果你只想要&lt;statusCodex&gt; 节点,然后抓住&lt;processStatus&gt; 节点,然后在你的选择中你可以这样做:status1 = (string)processStatus.Element(x + "statusCode4"); - (string) 将对元素进行转换值,如果元素不存在则简单地返回 null。 是的,你说的是对的。这是小 xml,但我正在处理大约 800 到 1000 行的大 xml。解析必须有超过 5 个连接,我无法预测缺少哪个标签。如果能够像我一样获得异常消息,但我可以直接到达那个地方,然后我提出你的解决方案。请让我知道你想要更多详细信息 【参考方案1】:

没有简单的方法(据我所知)可以准确地找出 LINQ to XML 语句中缺少/缺少哪些元素。但是,您可以做的是在元素上使用 (string) 来处理缺失的元素 - 但如果您有一系列元素,这可能会变得很棘手。

这在您当前的代码中不起作用:

status5 = (string)***Reply.Element(x + "processStatus").Element(x + "statusCode5")

因为(字符串)仅适用于第一个元素,而第二个是缺少的元素。

您可以将 LINQ 更改为仅关注子节点,如下所示:

XNamespace x = "http://xml.stack.com/RRAND01234";
var result = from ***Reply in XDocument.Parse(Myxml).Descendants(x + "processStatus")
         select new
         
            status1 = (string)***Reply.Element(x + "statusCode1"),
            status2 = (string)***Reply..Element(x + "statusCode2"),
            status3 = (string)***Reply..Element(x + "statusCode3"),
            status4 = (string)***Reply.Element(x + "statusCode4"),
            status5 = (string)***Reply.Element(x + "statusCode5"),
          ;

但是,如果您的 XML 很复杂并且您有不同的深度(嵌套元素),您将需要一个更强大的解决方案来避免一堆条件运算符检查或多个查询。

如果是这样的话,我有一些可能会有所帮助的东西 - 我必须把它挖掘出来。

编辑更复杂的 XML

我在工作中必须处理一些 XML 时遇到过类似的挑战。代替一种简单的方法来确定哪个节点是有问题的节点,并且不想拥有长得可怕的三元运算符,我编写了一个扩展方法,该方法从指定的起始节点递归地工作到我正在寻找的那个节点。

这里有一个有点简单和做作的例子来演示。

<SomeXML>
  <Tag1>
    <Tag1Child1>Value1</Tag1Child1>
    <Tag1Child2>Value2</Tag1Child2>
    <Tag1Child3>Value3</Tag1Child3>
    <Tag1Child4>Value4</Tag1Child4>
  </Tag1>
  <Tag2>
    <Tag2Child1>
      <Tag2Child1Child1>SomeValue1</Tag2Child1Child1>
      <Tag2Child1Child2>SomeValue2</Tag2Child1Child2>
      <Tag2Child1Child3>SomeValue3</Tag2Child1Child3>
      <Tag2Chidl1Child4>SomeValue4</Tag2Child1Child4>
    <Tag2Child1>
    <Tag2Child2>
      <Tag2Child2Child1>
        <Tag2Child2Child1Child1 />
        <Tag2Child2Child1Child2 />
    </Tag2Child2>
  </Tag2>
</SomeXML>

在上面的 XML 中,我无法知道(在解析之前)是否有任何子元素为空,所以经过一番搜索和摆弄后,我想出了以下扩展方法:

public static XElement GetChildFromPath(this XElement currentElement, List<string> elementNames, int position = 0)


    if (currentElement == null || !currentElement.HasElements)
    
        return currentElement;
    

    if (position == elementNames.Count - 1)
    
        return currentElement.Element(elementNames[position]);
    
    else
    
        XElement nextElement = currentElement.Element(elementNames[position]);
        return GetChildFromPath(nextElement, elmenentNames, position + 1);
    

基本上,该方法采用 XElement 调用它,加上路径顺序的元素的 List&lt;string&gt;,我想要的最后一个,以及一个位置(列表中的索引),然后沿着路径向下工作,直到找到有问题的元素或路径中的元素用完。它不像我希望的那样优雅,但我还没有时间重构它。

我会这样使用它(基于上面的示例 XML):

MyClass myObj = (from x in XDocument.Parse(myXML).Descendants("SomeXML")
                select new MyClass() 
                    Tag1Child1 = (string)x.GetChildFromPath(new List<string>() 
                                 "Tag1", "Tag1Child1" ),
                    Tag2Child1Child4 = (string)x.GetChildFromPath(new List<string>() 
                                 "Tag2", "Tag2Child1", "Tag2Child1Child4" ),
                    Tag2Child2Child1Child2 = (string)x.GetChildFromPath(new List<string>() 
                                 "Tag2", "Tag2Child2", "Tag2Child2Child1", 
                                 "Tag2Child2Child1Child2" )
                ).SingleOrDefault();

不像我希望的那样优雅,但至少它允许我解析可能缺少节点的 XML 文档,而不会破坏块。另一种选择是执行以下操作:

Tag2Child2Child1Child1 = x.Element("Tag2") == null ? 
                         "" : x.Element("Tag2Child2") == null ? 
                         "" : x.Element("Tag2Child2Child1") == null ? 
                         "" : x.Element("Tag2Child2Child1Child2") == null ? 
                         "" : x.Element("Tag2")
                               .Element("Tag2Child2")
                               .Element("Tag2Child2Child1") 
                               .Element("Tag2Child2Child1Child2").Value

对于具有数十个属性的对象来说,这将变得非常丑陋。

无论如何,如果这对您有用,请随时根据需要使用/调整/修改。

【讨论】:

我放了简单的xml让人们理解我的问题。我的 xml 很复杂,并且有不同的深度。如果错过了最后一个孩子但不适用于任何父节点,则您的答案有效。我尝试了很多方法,现在我决定是否能够在异常日志中丢失元素名称将对我有 90% 的帮助。感谢您的考虑。 @YasinJamesBond - 我添加了一些我开发并在工作中用于类似情况的东西。希望对您有所帮助。

以上是关于XML linq 需要有关异常的详细信息的主要内容,如果未能解决你的问题,请参考以下文章

获取有关 Airflow on_failure_callback 上下文的异常详细信息

更新条目时出错。有关详细信息,请参阅内部异常 [关闭]

实体异常:在提供程序连接上启动事务时发生错误。有关详细信息,请参阅内部异常

EF 更新条目时出错。有关详细信息,请参见内部异常。

linq 在查询表达式中处理异常

System.Xml.XmlException: “=”是意外的标记。标记应为“;”