【问题标题】:How do I build an xml section and save to file (not a valid xml document per se)如何构建 xml 部分并保存到文件(本身不是有效的 xml 文档)
【发布时间】:2016-07-08 07:05:10
【问题描述】:

我正在尝试构建 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>", ""));

【问题讨论】:

    标签: c# xml


    【解决方案1】:

    您无法创建无效的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&lt;XElement&gt; 的方法,您必须稍微更改定义以及添加其他项的方式:

    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 我刚刚用一种可能的解决方案更新了我的答案,以使用返回多个 XElement 实例的两种方法。
    • 非常感谢您的更新。它按照您建议的方式工作。
    • 我最终回到 XDocument 并添加了一个虚假的 元素,然后我在写入文件之前将其从字符串构建器中删除。原因是您建议的解决方案仅支持在第一级调用我的辅助方法,而在现实生活中,我也在更深层次调用辅助方法。
    【解决方案2】:

    您可以声明您希望 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>"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      相关资源
      最近更新 更多