繁体   English   中英

使用 Open XML 将 XML 文件中的数据读取到 Word 文档中,创建一个表格

[英]Create a table with the data read from XML file into word document using Open XML

我正在尝试从 XML 文件中读取数据,并希望在填充了相同数据的 Word 文档中生成表格。 我使用下面的 XML 文件和 C# 代码来做同样的事情。

我使用下面的代码在word文档中创建表格

class DocumentCreation :IDisposable
{
    private MemoryStream _ms;
    private WordprocessingDocument _wordprocessingDocument;

    public DocumentCreation()
    {
        _ms = new MemoryStream();
        _wordprocessingDocument = WordprocessingDocument.Create(_ms, WordprocessingDocumentType.Document);
        var mainDocumentPart = _wordprocessingDocument.AddMainDocumentPart();
        Body body = new Body();
        mainDocumentPart.Document = new Document(body);
        ApplyHeader(_wordprocessingDocument);
        InsertTable(_wordprocessingDocument);
    }


    private void InsertTable(WordprocessingDocument wordprocessingDocument)
    {
        string xmlFile = @"C:\wordDoc\movies.xml";   
        MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;

        // How to read the XML and create a table in word document filled with data from XML
    }

    public static void ApplyHeader(WordprocessingDocument doc)
    {
        // Get the main document part.
        MainDocumentPart mainDocPart = doc.MainDocumentPart;

        HeaderPart headerPart1 = mainDocPart.AddNewPart<HeaderPart>("r97");
        .......
        ......
        ......
    }
    public void AddBulletList(List<Run> runList)
    {
        .......
        .......
     }
  }

下面是我在课堂上打电话的地方

   static void Main(string[] args)
    {

        string fileToCreate = @"C:\wordDoc\test.docx";

        if (File.Exists(fileToCreate))
            File.Delete(fileToCreate);

        var writer = new DocumentCreation();
        List<string> fruitList = new List<string>() { "This is bulleted point 1", "This is bulleted point 2", "This is bulleted point 3" };

        writer.AddBulletList(fruitList);
        writer.SaveToFile(fileToCreate);
    }

我正在寻找一种在 Word 文档中创建表格的方法,其中包含来自 XML 文档上方的填充数据。

您可能想要的是一个 Open XML Tablew:tbl元素),如下所示:

<w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>Name</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>Released</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>Crash</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>2005</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>The Departed</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>2006</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>The Pursuit of Happiness</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>2006</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>The Bucket List</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>2007</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
</w:tbl>

上面的w:tbl是通过以下CanCreateTableFromXml单元测试从您的 XML 创建的,它只是调用TransformMovies(XElement)纯函数转换方法。 后者基于第一部电影和每部电影的一个内容行创建一个标题行。

        private const string MoviesXml =
            @"<?xml version=""1.0"" encoding=""UTF-8""?>
<Movies>
  <Movie>
    <Name>Crash</Name>
    <Released>2005</Released>
  </Movie>
  <Movie>
    <Name>The Departed</Name>
    <Released>2006</Released>
  </Movie>
  <Movie>
    <Name>The Pursuit of Happiness</Name>
    <Released>2006</Released>
  </Movie>
  <Movie>
    <Name>The Bucket List</Name>
    <Released>2007</Released>
  </Movie>
</Movies>";

        private static Table TransformMovies(XElement movies)
        {
            var headerRow = new[]
            {
                new TableRow(movies
                    .Elements()
                    .First()
                    .Elements()
                    .Select(e => new TableCell(new Paragraph(new Run(new Text(e.Name.LocalName))))))
            };
            var movieRows = movies.Elements().Select(TransformMovie);

            return new Table(headerRow.Concat(movieRows));
        }

        private static OpenXmlElement TransformMovie(XElement element)
        {
            return element.Name.LocalName switch
            {
                "Movie" => new TableRow(element.Elements().Select(TransformMovie)),
                _ => new TableCell(new Paragraph(new Run(new Text(element.Value)))),
            };
        }

        [Fact]
        public void CanCreateTableFromXml()
        {
            var movies = XElement.Parse(MoviesXml);
            var table = TransformMovies(movies);
        }

2019-12-29 更新:如何调用 TransformMovies

下面的示例演示如何在InsertTable方法中调用TransformMovies方法。

        private void InsertTable(WordprocessingDocument wordprocessingDocument)
        {
            const string xmlFile = @"Resources\Movies.xml";
            MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;

            // How to read the XML and create a table in word document filled 
            // with data from XML

            // First, read the Movies XML string from your XML file.
            string moviesXml = File.ReadAllText(xmlFile);

            // Second, create the table as previously shown in the unit test method.
            XElement movies = XElement.Parse(moviesXml);
            Table table = TransformMovies(movies);

            // Third, append the Table to your empty Body.
            mainPart.Document.Body.AppendChild(table);
        }

2019-12-30 更新:如何添加表格边框

查看您尝试添加表格边框的已更改TransformMovies方法,此方法生成以下 Open XML 表格标记:

<w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:tr>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>Name</w:t>
        </w:r>
      </w:p>
    </w:tc>
    <w:tc>
      <w:p>
        <w:r>
          <w:t>Released</w:t>
        </w:r>
      </w:p>
    </w:tc>
  </w:tr>
  <w:tblPr>
    <w:tblBorders>
      <w:top w:val="single" w:sz="24" />
      <w:bottom w:val="single" w:sz="24" />
      <w:left w:val="single" w:sz="24" />
      <w:right w:val="single" w:sz="24" />
      <w:insideH w:val="single" w:sz="24" />
      <w:insideV w:val="single" w:sz="24" />
    </w:tblBorders>
  </w:tblPr>
</w:tbl>

当电影行丢失时,我们只能看到标题行。 w:tblPr元素( TableProperties实例)被添加为w:tblTable )的最后一个子元素。

为什么你不能渲染数据(或者为什么那些电影行丢失了)? 您没有在更改后的TransformMovies方法中将movieRows添加到headerRow Concat是一种不会改变headerRow的纯方法。 它只返回一个连接的序列,但不更改第一个序列(在本例中为headerRow )。

为什么不能在表格中添加边框线? 您正在添加TableProperties作为Table的最后一个子元素。 但是,根据 Open XML 标准,它必须是第一个。 虽然 Microsoft Word 经常容忍无效的 Open XML 标记,但在这种情况下它不会这样做。

以下摘自您更改后的TransformedMovies方法指出了导致这些问题的错误:

Table table = new Table();

var headerRow = new[]
{
    new TableRow(movies
        .Elements()
        .First()
        .Elements()
        .Select(e => new TableCell(new Paragraph(new Run(new Text(e.Name.LocalName))))))
};
var movieRows = movies.Elements().Select(TransformMovie);

// The following line of code is the first culprit.
// The return value of the pure method is not used (Visual Studio should show a warning).
// The headerRow array is unchanged.
headerRow.Concat(movieRows);

TableProperties tblProp = ...

// At this point, you append the unchanged header row.
table.Append(headerRow);

// The following line of code is the second culprit.
// tblProp is appended as the last child of table. It must be the first one.
table.AppendChild(tblProp);

您应该按如下方式重写您的方法以使其工作:

private static Table TransformMovies(XElement movies)
{
    var table = new Table();

    var tblPr = new TableProperties(
        new TableBorders(
            new TopBorder { Val = BorderValues.Single, Size = 24 },
            new BottomBorder { Val = BorderValues.Single, Size = 24 },
            new LeftBorder { Val = BorderValues.Single, Size = 24 },
            new RightBorder { Val = BorderValues.Single, Size = 24 },
            new InsideHorizontalBorder { Val = BorderValues.Single, Size = 24 },
            new InsideVerticalBorder { Val = BorderValues.Single, Size = 24 }));

    var headerRow = new TableRow(movies
        .Elements()
        .First()
        .Elements()
        .Select(e => new TableCell(new Paragraph(new Run(new Text(e.Name.LocalName))))));

    IEnumerable<OpenXmlElement> movieRows = movies.Elements().Select(TransformMovie);

    // Append child elements in the right order.
    table.AppendChild(tblPr);
    table.AppendChild(headerRow);
    table.Append(movieRows);

    return table;
}

请注意,我在之前的回答中创建了一个headerRow数组,然后在以下 return 语句中使用headerRow.Concat(movieRows)

// Return Table with IEnumerable<OpenXmlElement> added to it.
return new Table(headerRow.Concat(movieRows));

我这样做是因为我喜欢函数式编程方法,但不幸的是,Open XML SDK 提供的强类型类的构造函数不允许您按照以下返回语句的要求混合单个OpenXmlElementIEnumerable<OpenXmlElement>实例(工作,你要知道):

// Return Table with OpenXmlElement and IEnumerable<OpenXmlElement> added to it.
return new Table(headerRow, movieRows);

现在,如果您必须添加多个单独的实例(即TablePropertiesTableRow )和一个集合(即IEnumerable<OpenXmlElement> ),您也可以使用AppendChild (对于单个实例)和Append (对于集合)方法。 在这种情况下,您不需要为单个实例创建数组或列表来连接它们并将它们一次性传递给构造函数。

这是使用GemBox.Document的另一种方法,它可以简化 DOCX 文件的处理(创建、编辑、打印、转换等...):

string xmlFile = @"C:\wordDoc\movies.xml";
string docxFile = @"C:\wordDoc\test.docx";

var document = new DocumentModel();

var section = new Section(document);
document.Sections.Add(section);

var table = new Table(document);
section.Blocks.Add(table);

foreach (var xmlRow in XDocument.Load(xmlFile).Descendants("Movie"))
    table.Rows.Add(
        new TableRow(document,
            new TableCell(document,
                new Paragraph(document, xmlRow.Element("Name").Value)),
            new TableCell(document,
                new Paragraph(document, xmlRow.Element("Released").Value))));

table.TableFormat.Borders.SetBorders(MultipleBorderTypes.All, BorderStyle.Double, Color.Red, 2);

document.Save(docxFile);

生成的“test.docx”文件如下所示:

在此处输入图片说明

此外,您可以轻松地从“fruitList”添加这些列表项。
例如,请参阅列表示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM