Linq-to-XML XElement.Remove()会留下不需要的空格

Joe*_*ung 10 c# xml xelement linq-to-xml

我有一个从字节数组创建的XDocument(通过tcp/ip接收).

然后我搜索特定的xml节点(XElements),并在通过调用XElement.Remove()从Xdocument中检索值'pop'之后.在我的所有解析完成后,我希望能够记录我没有解析的xml(XDocument中的剩余xml).问题是在调用XElement.Remove()时会留下额外的空格.我想知道删除这个额外空格的最佳方法,同时保留剩余xml中的其余格式.

示例/示例代码

如果我通过套接字收到以下xml:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications with XML.</description>
   </book>
</catalog>
Run Code Online (Sandbox Code Playgroud)

我使用以下代码来解析此xml并删除一些XElements:

private void socket_messageReceived(object sender, MessageReceivedEventArgs e)
{
     XDocument xDoc;
     try
     {
         using (MemoryStream xmlStream = new MemoryStream(e.XmlAsBytes))
         using (XmlTextReader reader = new XmlTextReader(xmlStream))
         {
             xDoc = XDocument.Load(reader);
         }

         XElement Author = xDoc.Root.Descendants("author").FirstOrDefault();
         XElement Title  = xDoc.Root.Descendants("title").FirstOrDefault();
         XElement Genre  = xDoc.Root.Descendants("genre").FirstOrDefault();

         // Do something with Author, Title, and Genre here...

         if (Author != null) Author.Remove();
         if (Title  != null) Title.Remove();
         if (Genre  != null) Genre.Remove();

         LogUnparsedXML(xDoc.ToString());

     }
     catch (Exception ex)
     {
         // Exception Handling here...
     }
}
Run Code Online (Sandbox Code Playgroud)

然后发送到LogUnparsedXML消息的结果xml字符串将是:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">



      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications with XML.</description>
   </book>
</catalog>
Run Code Online (Sandbox Code Playgroud)

在这个人为的例子中,这似乎不是什么大问题,但在我的实际应用中,剩下的xml看起来很邋..我尝试使用XDocument.ToString重载,使SaveOptions枚举无效.我还尝试使用SaveOptions枚举调用xDoc.Save保存到文件.我确实尝试XElement.Nodes().OfType<XText>()过尝试删除空格的几个不同的linq查询,但我常常把我希望保留的空白与我想要删除的空白一起.

在此先感谢您的帮助.

Fré*_*idi 7

用可移植的方式回答并不容易,因为解决方案在很大程度上取决于如何XDocument.Load()生成空白文本节点(并且围绕LINQ to XML的几种实现可能对此微妙的细节意见不一致)。

也就是说,您似乎永远都不会从元素中删除最后一个孩子(<description><book>。如果确实如此,那么我们就不必担心父元素的结束标记的缩进,我们可以删除该元素及其所有后续文本节点,直到到达另一个元素为止。TakeWhile()将完成这项工作。

编辑:嗯,看来您毕竟需要删除最后一个孩子。因此,事情将变得更加复杂。下面的代码实现以下算法:

  • 如果该元素不是其父元素的最后一个元素:
    • 删除所有随后的文本节点,直到到达下一个元素。
  • 除此以外:
    • 删除以下所有文本节点,直到找到一个包含换行符的节点,
    • 如果该节点仅包含换行符:
      • 删除该节点。
    • 除此以外:
      • 创建一个仅包含在换行符之后找到的空格的新节点,
      • 在原始节点之后插入该节点,
      • 删除原始节点。
  • 删除元素本身。

结果代码为:

public static void RemoveWithNextWhitespace(this XElement element)
{
    IEnumerable<XText> textNodes
        = element.NodesAfterSelf()
                 .TakeWhile(node => node is XText).Cast<XText>();
    if (element.ElementsAfterSelf().Any()) {
        // Easy case, remove following text nodes.
        textNodes.ToList().ForEach(node => node.Remove());
    } else {
        // Remove trailing whitespace.
        textNodes.TakeWhile(text => !text.Value.Contains("\n"))
                 .ToList().ForEach(text => text.Remove());
        // Fetch text node containing newline, if any.
        XText newLineTextNode
            = element.NodesAfterSelf().OfType<XText>().FirstOrDefault();
        if (newLineTextNode != null) {
            string value = newLineTextNode.Value;
            if (value.Length > 1) {
                // Composite text node, trim until newline (inclusive).
                newLineTextNode.AddAfterSelf(
                    new XText(value.SubString(value.IndexOf('\n') + 1)));
            }
            // Remove original node.
            newLineTextNode.Remove();
        }
    }
    element.Remove();
}
Run Code Online (Sandbox Code Playgroud)

从那里,您可以执行以下操作:

if (Author != null) Author.RemoveWithNextWhitespace();
if (Title  != null) Title.RemoveWithNextWhitespace();
if (Genre  != null) Genre.RemoveWithNextWhitespace();
Run Code Online (Sandbox Code Playgroud)

尽管我建议您将上述内容替换为类似数组或params方法调用的循环,以避免代码冗余。