Search code examples
c#xmlwebapiopenxml-sdkasp.net-core-6.0

I want to create docx file from XML string that I receive from an API with ASP.NET Core 6


This is my xml file that I get this from docx file that I zipped.

This is the document.xml:

<w:document 
xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" 
xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" 
xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" 
xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" 
xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" 
xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" 
xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" 
xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" 
xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" 
xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" 
xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" 
xmlns:o="urn:schemas-microsoft-com:office:office" 
xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" 
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" 
xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" 
xmlns:v="urn:schemas-microsoft-com:vml" 
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" 
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" 
xmlns:w10="urn:schemas-microsoft-com:office:word" 
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" 
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" 
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" 
xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" 
xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" 
xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" 
xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" 
xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" 
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" 
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" 
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" 
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" 
mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh wp14">
<w:body>
<w:p w14:paraId="59E8928B" w14:textId="4F989F00" w:rsidR="00E57A39" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="1"/>
</w:numPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="008F41DD">
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
<w:t xml:space="preserve">One </w:t>
</w:r>
</w:p>
<w:p w14:paraId="2D18B139" w14:textId="40831F20" w:rsidR="008F41DD" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="1"/>
</w:numPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="008F41DD">
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
<w:t>Two</w:t>
</w:r>
</w:p>
<w:p w14:paraId="4E1C2299" w14:textId="45351C9E" w:rsidR="008F41DD" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="1"/>
</w:numPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="008F41DD">
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
<w:t>Three</w:t>
</w:r>
</w:p>
<w:p w14:paraId="07C809C6" w14:textId="10C92B2E" w:rsidR="008F41DD" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="1"/>
</w:numPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="008F41DD">
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
<w:t>Four</w:t>
</w:r>
</w:p>
<w:p w14:paraId="2DB3ADA1" w14:textId="2F165FEA" w:rsidR="008F41DD" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
</w:p>
<w:p w14:paraId="086A6010" w14:textId="77777777" w:rsidR="008F41DD" w:rsidRPr="008F41DD" w:rsidRDefault="008F41DD" w:rsidP="008F41DD">
<w:pPr>
<w:rPr>
<w:rFonts w:cs="B Nazanin"/>
</w:rPr>
</w:pPr>
</w:p>
<w:sectPr w:rsidR="008F41DD" w:rsidRPr="008F41DD">
<w:pgSz w:w="12240" w:h="15840"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
<w:cols w:space="720"/>
<w:docGrid w:linePitch="360"/>
</w:sectPr>
</w:body>
</w:document>

My project endpoint API and code:

[HttpPost("[action]")]
[Consumes("application/xml")]
public IActionResult UploadXml([FromBody] XElement xml)
{
    MemoryStream generatedDocument = new MemoryStream();

    using (WordprocessingDocument package = WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
    {
        var mainPart = package.AddMainDocumentPart();

        var doc = new XmlDocument();
        doc.LoadXml(xml.ToString());

        new Document(doc.OuterXml);
        package.Save();
    }

    var result = generatedDocument.ToArray();

    return File(result, "application/octet-stream", "document.docx");
}

Now I want to pass the all this XML as string to OpenXML SDK package but I don't know how.

I searched a lot but I can't solve this problem and I need an example code to show me how to do this.

If anybody has done this before, please share your code with me.

I want to say that I know there is so much docs out there but I should do this fast and I can't read Microsoft bad docs.

Thanks


Solution

  • First, your created Document instance goes no where in the code you show. If you don't add it to the package you'll end with no content in your document.

    Here is the working code with the needed changes made, basically removing the unneeded fiddling with XmlDocument and the unneeded creation of a byte array because File does accept a stream.

    [HttpPost()]
    [Consumes("application/xml")]
    public IActionResult UploadXml([FromBody] XElement xml)
    {
        MemoryStream generatedDocument = new MemoryStream();
    
        using (WordprocessingDocument package = WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
        {
            var mainPart = package.AddMainDocumentPart();
    
            mainPart.Document = new Document(xml.ToString(SaveOptions.DisableFormatting));
            package.Save();
        }
    
        generatedDocument.Position = 0; // to start of stream!
    
        return File(generatedDocument, "application/octet-stream", "document.docx");
    }
    

    I assume you are aware that if you want the Action to accept an XElement in .Net Core you have to enable the Xml Serializers like so:

    public void ConfigureServices(IServiceCollection services)
    {
        services
           .AddControllersWithViews( opt => { opt.MaxValidationDepth = 64; })
           .AddXmlSerializerFormatters();
    }
    
    

    I also had to set the MaxValidationDepth to a higher value then the default 32. Doubling worked for the example data.

    For completeness here is my curl test command

    curl --trace con -H"Content-Type: application/xml" -d@worddoc.xml  --output doctest.docx  http://localhost:5000/Home/UploadXml
    

    And this is what I end up with:

    enter image description here