XDocument.ToString()会丢失XML编码标记。

118

有没有办法在toString()函数中获取XML编码?

示例:

xml.Save("myfile.xml");

导致

<?xml version="1.0" encoding="utf-8"?>
<Cooperations>
  <Cooperation>
    <CooperationId>xxx</CooperationId>
    <CooperationName>Allianz Konzern</CooperationName>
    <LogicalCustomers>
< p >但是< /p >
tb_output.Text = xml.toString();

导致输出如下所示

<Cooperations>
  <Cooperation>
    <CooperationId>xxx</CooperationId>
    <CooperationName>Allianz Konzern</CooperationName>
    <LogicalCustomers>
    ...
9个回答

104

要么明确编写声明,要么使用 StringWriter 并调用 Save()

using System;
using System.IO;
using System.Text;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        string xml = @"<?xml version='1.0' encoding='utf-8'?>
<Cooperations>
  <Cooperation />
</Cooperations>";

        XDocument doc = XDocument.Parse(xml);
        StringBuilder builder = new StringBuilder();
        using (TextWriter writer = new StringWriter(builder))
        {
            doc.Save(writer);
        }
        Console.WriteLine(builder);
    }
}

你可以轻松地将其添加为扩展方法:

public static string ToStringWithDeclaration(this XDocument doc)
{
    if (doc == null)
    {
        throw new ArgumentNullException("doc");
    }
    StringBuilder builder = new StringBuilder();
    using (TextWriter writer = new StringWriter(builder))
    {
        doc.Save(writer);
    }
    return builder.ToString();
}

这种方式的优点在于它不会出现错误,即使没有声明也是如此 :)

然后你可以使用:

string x = doc.ToStringWithDeclaration();
请注意,这将使用UTF-16作为编码,因为这是 StringWriter 中的隐式编码。不过您可以通过创建 StringWriter 的子类来自己影响编码,例如始终使用UTF-8

14
这里有一个小问题,就是在保存时,XDocument声明中的编码会被忽略并由StringWriter的编码替换,这可能是您想要的,也可能不是。 - Sam Holder
2
然后,您将扩展方法与来自https://dev59.com/9HI_5IYBdhLWcg3wBuJs#1564727的Utf8StringWriter结合使用 ;) - Nick Josevski
12
使用上述的扩展方法更容易,但会返回以下内容: return doc.Declaration + doc.ToString(); 如果Declaration为空,则返回空字符串。 - Steve G.
@IlyaLuzyanin:请参见https://dev59.com/XXNA5IYBdhLWcg3wcdhk#955698 - 我会将其编辑到答案中。 - Jon Skeet
@IlyaLuzyanin:然后你需要检测现有的编码(将在文档中)并创建一个声称具有相同编码的StringWriter子类的实例,基本上是这样。 - Jon Skeet
显示剩余3条评论

56

Declaration属性将包含XML声明。要获取内容和声明,可以按照以下步骤进行:

tb_output.Text = xml.Declaration.ToString() + xml.ToString()

7
如果您在XDocument中不使用新的XDeclaration("1.0", "utf-8", "yes"),似乎会出现错误,因为xml.Declaration为空。但是,xml.save似乎会自动检测正确的编码。 - Henrik P. Hessel
或者,tb_output.Text = @"<?xml version=""1.0"" encoding=""utf-8"" ?>" + xml; - Bill Hoag
4
或者 ... = $"{xdoc.Declaration}{Environment.NewLine}{xdoc}"; 这行代码将一个XML文档的声明和内容转换成一个字符串。其中,$符号表示插入变量。xdoc.Declaration表示XML文档的声明部分,xdoc表示XML文档的内容部分。Environment.NewLine表示换行符。 - WernerCD

13

使用这个:

output.Text = String.Concat(xml.Declaration.ToString() , xml.ToString())

2
如果不创建新的XDeclaration("1.0", "utf-8", "yes")并将其添加到XDocument或其他对象中,xml.Declaration.ToString()将抛出空异常。 - Ziggler
1
输出以下代码更为安全,因为 Concat 方法不会关注空字符串:output.Text = String.Concat(xml.Declaration, xml)。 - dmihailescu

3

我就是这样做的

        string distributorInfo = string.Empty;

        XDocument distributors = new XDocument();

     //below is important else distributors.Declaration.ToString() throws null exception
        distributors.Declaration = new XDeclaration("1.0", "utf-8", "yes"); 

        XElement rootElement = new XElement("Distributors");
        XElement distributor = null;
        XAttribute id = null;

        distributor = new XElement("Distributor");
        id = new XAttribute("Id", "12345678");
        distributor.Add(id);
        rootElement.Add(distributor);

        distributor = new XElement("Distributor");
        id = new XAttribute("Id", "22222222");

        distributor.Add(id);

        rootElement.Add(distributor);         

        distributors.Add(rootElement);

        distributorInfo = String.Concat(distributors.Declaration.ToString(), distributors.ToString());

请查看下面我在distributorInfo中获取的内容:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Distributors>
  <Distributor Id="12345678" />
  <Distributor Id="22222222" />
  <Distributor Id="11111111" />
</Distributors>

1
好的例子。一些注意事项:1)使用new XDeclaration(“1.0”,“utf-8”)代替new XDeclaration(“1.0”,“utf-8”,“yes”),2)在最后一行插入新行:distributors.Declaration.ToString() + Environment.NewLine + distributors.ToString()。 - Alexey Obukhov

2
与其他+1答案类似,但关于声明的细节更多,连接略微更准确。
在格式化的XML中,“”声明应该单独放在一行上,因此我确保我们添加了换行符。 注意:使用“Environment.Newline”,以便生成特定于平台的换行符。
// Parse xml declaration menthod
XDocument document1 =
  XDocument.Parse(@"<?xml version=""1.0"" encoding=""iso-8859-1""?><rss version=""2.0""></rss>");
string result1 =
  document1.Declaration.ToString() +
  Environment.NewLine +
  document1.ToString() ;

// Declare xml declaration method
XDocument document2 = 
  XDocument.Parse(@"<rss version=""2.0""></rss>");
document2.Declaration =
  new XDeclaration("1.0", "iso-8859-1", null);
string result2 =
  document2.Declaration.ToString() +
  Environment.NewLine +
  document2.ToString() ;

两种结果都会产生:

<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0"></rss>

2

这些答案中有一些解决了发帖者的问题,但似乎过于复杂。这里有一个简单的扩展方法,避免了需要单独编写writer的需求,处理了缺失声明并支持标准的ToString SaveOptions参数。

public static string ToXmlString(this XDocument xdoc, SaveOptions options = SaveOptions.None)
{
    var newLine =  (options & SaveOptions.DisableFormatting) == SaveOptions.DisableFormatting ? "" : Environment.NewLine;
    return xdoc.Declaration == null ? xdoc.ToString(options) : xdoc.Declaration + newLine + xdoc.ToString(options);
}

要使用该扩展程序,只需将xml.ToString()替换为xml.ToXmlString()


0

你也可以使用 XmlWriter 并调用

Writer.WriteDocType() 

方法。


0
string uploadCode = "UploadCode";
string LabName = "LabName";
XElement root = new XElement("TestLabs");
foreach (var item in returnList)
{  
       root.Add(new XElement("TestLab",
                new XElement(uploadCode, item.UploadCode),
                new XElement(LabName, item.LabName)
                            )
               );
}

XDocument returnXML = new XDocument(new XDeclaration("1.0", "UTF-8","yes"),
             root);

string returnVal;
using (var sw = new MemoryStream())
{
       using (var strw = new StreamWriter(sw, System.Text.UTF8Encoding.UTF8))
       {
              returnXML.Save(strw);
              returnVal = System.Text.UTF8Encoding.UTF8.GetString(sw.ToArray());
       }
}

// ReturnVal has the string with XML data with XML declaration tag

0

扩展方法用于获取包含Xml声明的内容,在此处使用字符串插值,并选择在Xml声明后添加新行,因为我想这是标准做法。

public static class XDocumentExtensions {
        
public static string ToStringIncludeXmlDeclaration(this XDocument doc){
               return $"({((doc.Declaration != null ? doc.Declaration.ToString() + 
     Environment.NewLine : string.Empty) + doc.ToString())}";
    }  
  }
}

使用方法:

tb_output.Text = xml.ToStringIncludeXmlDeclaration();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接