如何构建xml节并保存到文件(本身不是有效的xml文档)

时间:2016-07-08 07:05:11

标签: c# xml

我正在尝试构建XML文档的一小部分,以包含在最终文档中。 使用XDocument时,我得到一个例外:

  

“此操作会创建一个结构不正确的文档。”

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

由于文件要包含在较大的文档中,因此我的基本元素不会以最终文档的根目录结束。

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 个答案:

答案 0 :(得分: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();

编辑:要使用在IEnumerable<XElement>的对象初始值设定项中返回List的方法,您必须稍微更改定义以及添加其他项的方式:

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))

答案 1 :(得分: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();

或添加aritificial root元素:

"<aritificialroot>" + fragment + "</aritificialroot>"