如何构建一个XML部分并保存到文件(不是一个有效的XML文档)

3

我试图构建一个XML文档的一部分,以便包含在最终文档中。

使用XDocument时,我遇到了异常:

"This operation would create an incorrectly structured document."

这是因为我的片段有多个“根”节点。

由于该文件将被包含在一个更大的文档中,因此我的基本元素不会出现在最终文档的根目录中。

XDocument constsDocument = new XDocument(
    new XComment($" Consts section generated on {DateTime.Now} "),
    new XComment($" First group of constants. "),
    FirstTextConsts(MyFirstCollection),
    new XComment($" Refrigerant constants. "),
    SecondTextConsts(MySecondCollection)
    );
// To avoid xml file starting with <?xml version="1.0" encoding="utf-8"?> use stringbuilder and StreamWriter.
StringBuilder sb = new StringBuilder();
XmlWriterSettings xws = new XmlWriterSettings
{
    OmitXmlDeclaration = true,
    Indent = true
};
using (XmlWriter xw = XmlWriter.Create(sb, xws))
{
    constsDocument.Save(xw);
}
System.IO.StreamWriter file = new System.IO.StreamWriter(_outputFileName);
file.WriteLine(sb.ToString()); 
file.Close();

编辑1: 在文档创建过程中调用的两种方法(主要):

private static IEnumerable<XElement> FirstTextConsts(IEnumerable<MyClass> collection)
{
    return collection.Select(r => new XElement("const", 
            new XAttribute("name", $"IDENTIFIER_{r.Id}"),
            new XAttribute("translatable", "false"),
            new XElement("text",
                new XAttribute("lang","en"), r.Name)));            
}

private static IEnumerable<XElement> SecondTextConsts(IEnumerable<MyClass> collection)
{
    foreach (var obj in collection)
    {
        if (obj.SomeSelector)
            yield return new XElement("const", 
                new XAttribute("name", $"IDENTIFIER_{r.Id}"),
                new XAttribute("translatable", "false"),
                new XElement("text",
                    new XAttribute("lang","en"), r.Name)));            
        if (obj.SomeOtherSelector)
            yield return new XElement("const", 
                new XAttribute("name", $"IDENTIFIER_{r.Id}"),
                new XAttribute("translatable", "true")
                );            
    }
}

编辑2: 由于我需要使用XDocument的灵活性来构建一个多级xml文档,并且希望在不同级别上返回IEnumerable类型的帮助方法,因此我决定添加一个虚假的元素,在写入文件之前再将其删除:

XDocument constsDocument = new XDocument(
    new XElement("root", 
        new XComment($" Consts section generated on {DateTime.Now} "),
        new XComment($" First group of constants. "),
        FirstTextConsts(MyFirstCollection),
        new XComment($" Refrigerant constants. "),
        SecondTextConsts(MySecondCollection)
        )
    );

在写入文件之前,我会去掉元素中的内容:
    file.WriteLine(sb.ToString().Replace("<root>" + Environment.NewLine, "").Replace(Environment.NewLine + "</root>", ""));
2个回答

3

您无法创建一个无效的XDocument(由于多个“根”节点)。因此,您需要创建一个节点列表,并将其写入文档片段。

var constsDocument = new List<XNode> {
           new XComment($" Consts section generated on {DateTime.Now} "),
           new XComment($" First group of constants. "),
           new XElement("example"),
           new XComment($" Refrigerant constants. "),
           new XElement("another")
};
// To avoid xml file starting with <?xml version="1.0" encoding="utf-8"?> use stringbuilder and StreamWriter.
StringBuilder sb = new StringBuilder();
XmlWriterSettings xws = new XmlWriterSettings
{
    OmitXmlDeclaration = true,
    Indent = true,
    ConformanceLevel = ConformanceLevel.Fragment
};
using (XmlWriter xw = XmlWriter.Create(sb, xws))
{
    foreach (var element in constsDocument)
    {
        element.WriteTo(xw);
    }
}
System.IO.StreamWriter file = new System.IO.StreamWriter(_outputPath);
file.WriteLine(sb.ToString());
file.Close();

编辑:为了在List的对象初始化器中使用返回IEnumerable<XElement>的方法,您需要稍微更改定义和添加其他项目的方式:
var constsDocument = new List<IEnumerable<XNode>> {
    new [] { new XComment($" Consts section generated on {DateTime.Now} ") },
    new [] { new XComment($" First group of constants. ") },
    FirstTextConsts(MyFirstCollection),
    new [] { new XComment($" Refrigerant constants. ") },
    SecondTextConsts(MySecondCollection)
);

...

foreach (var element in constsDocument.SelectMany(n => n))

我已经编辑了我的问题,以说明在文档创建期间调用的两种方法。正如您所建议的那样,在对象初始化程序中它们不起作用? - TheRoadrunner
@TheRoadrunner 我刚刚更新了我的答案,提供了一种使用你的两个返回多个 XElement 实例的方法的可能解决方案。 - Keith Hall
非常感谢您的更新。按照您的建议,它已经可以正常工作了。 - TheRoadrunner
我最终回到了XDocument,并添加了一个虚假的<root>元素,然后在写入文件之前从字符串构建器中删除它。原因是您建议的解决方案仅支持在第一级上调用我的辅助方法,而在实际情况下,我还在更深层次上调用辅助方法。 - TheRoadrunner

1
你可以声明想要将XML作为一个片段,并从XDocument切换到XMLDocument:
XmlDocument constDocument = new XmlDocument();
constDocument.CreateComment(" Consts section generated on {DateTime.Now} ");
constDocument.CreateComment(" First group of constants. ");
FirstTextConsts(MyFirstCollection); // Need to be adapted
constDocument.CreateComment(" Refrigerant constants. ");
SecondTextConsts(MySecondCollection); // Need to be adapted
XmlDocumentFragment fragDocument = constDocument.CreateDocumentFragment();
fragDocument.InnerXml = "<const>fragment</const><const>fragment2</const>"; // Need to be adapted

StringBuilder sb = new StringBuilder();
XmlWriterSettings xws = new XmlWriterSettings
{
    ConformanceLevel = ConformanceLevel.Fragment,
    OmitXmlDeclaration = true,
    Indent = true
};
using (XmlWriter xw = XmlWriter.Create(sb, xws))
{
    fragDocument.WriteContentTo(xw);
}
System.IO.StreamWriter file = new System.IO.StreamWriter(_outputFileName);
file.WriteLine(sb.ToString()); 
file.Close();

或者添加人工根元素:
"<aritificialroot>" + fragment + "</aritificialroot>"

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