Home » excel » c# – Import xml using XML-mapping in OpenXML

c# – Import xml using XML-mapping in OpenXML

Posted by: admin April 23, 2020 Leave a comment

Questions:

I’ve been trying to import XML-data to Excel using an xml-mapping in OpenXML SDK.

I have found multiple examples of doing this using Excel Interop, but not using the SDK. As a side note, I have this working for Word-files, but the procedure is not the same in Excel (CustomXmlPart in Word, CustomXmlMappingPart in Excel).

Maybe it’s just not possible using the SDK directly. A workaround might be using named ranges, but I find that less practical.

Edit:
The scenario is using an Excel-file with an XML-mapping (created as per described https://support.office.com/en-us/article/Import-XML-data-6eca3906-d6c9-4f0d-b911-c736da817fa4?ui=en-US&rs=en-US&ad=US) as a “template” to fill with data. The could be Customer name, current date or likewise.

I have tried something similar to this. In contrast to Word, the mapping is shown in a CustomXmlMappingsPart.

using (var fileStream = File.Open(@"c:\temp\test.xslx", FileMode.Open))
{
   SpreadsheetDocument excelDoc = SpreadsheetDocument.Open(fileStream, true);
   // Only one mapping
   // When doing this for word, there is a document.MainDocumentPart.CustomXmlParts
   var mapping = excelDoc.WorkbookPart.GetPartsOfType<CustomXmlMappingsPart>().FirstOrDefault();
   XNamespace mappingNS = "http://demoimport.org/xmlimport";
   var xmlData = new XElement(mappingNS + "ImportRoot",
                              new XElement(mappingNS + "Customer", "Test Customer Name"));

   XmlReader reader = xmlData.CreateReader();
   reader.MoveToContent();

   using (MemoryStream ms = new MemoryStream())
   {
      xmlData.Save(ms);
      ms.Position = 0;
      // Corrupts the file
      // Probably due to CustomXmlMappingsPart
      mapping.FeedData(ms);
   }   
}
How to&Answers:

Disclaimer – I have no idea if this is the way to accomplish this, but the following works for my limited use case

Instead of working directly with XML-mapping parts (CustomXmlMappingsPart), using using the Open XML Productivity Tool, I discovered that the mapped cells contain XPath for the mapping in a collection of SingleCellTablePart.

It is possible to traverse all SingleCellTablePart to find a matching element in XML file. I found a really useful extensions from https://simpleooxml.codeplex.com/ to write data to the document. It
is perfectly possible to achieve only using the OpenXML SDK. But I found setting cell values using the SDK is not a trivial as one would think.

using (var fileStream = File.Open(@"c:\temp\test.xlsx", FileMode.Open))
{
    /* Simple xml-file, 
     * <General>
     *   <Customer>Demo Customer</Customer>
     * </General>
     */
    XDocument doc = XDocument.Load(@"c:\temp\demodata.xml");

    SpreadsheetDocument excelDoc = SpreadsheetDocument.Open(fileStream, true);

    foreach (var workSheet in excelDoc.WorkbookPart.WorksheetParts)
    {
        // Note, multiple mappings could exist. For simplicity I expect only one
        var mappings = workSheet.GetPartsOfType<SingleCellTablePart>().FirstOrDefault();
        if (mappings == null)
            continue;

        foreach (SingleXmlCell cellMapping in mappings.SingleXmlCells)
        {
            if (cellMapping.XmlCellProperties != null &&
                cellMapping.XmlCellProperties.XmlProperties != null)
            {
                // cellMapping.XmlCellProperties.XmlProperties.XPath  = /General/Customer
                var matchingNode = doc.XPathSelectElement(cellMapping.XmlCellProperties.XmlProperties.XPath);
                if(matchingNode != null && !string.IsNullOrEmpty(matchingNode.Value))
                {
                    // If a maching node exist, set text value from XML-file
                    // SpreadsheetReader and WorksheetWriter from https://simpleooxml.codeplex.com/
                    string column = SpreadsheetReader.ColumnFromReference(cellMapping.CellReference.Value);
                    uint row = SpreadsheetReader.RowFromReference(cellMapping.CellReference.Value);
                    WorksheetWriter.PasteText(excelDoc, workSheet, column, row, matchingNode.Value);
                }
            }
        }
        WorksheetWriter.Save(excelDoc, workSheet);
    }
}