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

Viewed 120

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

2 Answers

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

All of the Code I Used :

[HttpPost("[action]")]
    public async Task<IActionResult> UploadXml([FromForm] string XmlString, [FromForm] string FileName)
    {
        var mem = new MemoryStream();
        using (WordprocessingDocument package = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document))
        {
            MainDocumentPart mainPart = package.AddMainDocumentPart();
            using (Stream stream = mainPart.GetStream())
            {
                byte[] buf = (new UTF8Encoding()).GetBytes(XmlString);
                stream.Write(buf, 0, buf.Length);
            }
            package.Save();
        }
        return File(mem.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "document.docx");
   }

I Have Worked Alot on this , finally i find the way to do this as Below Code:

var mem = new MemoryStream();
        using (WordprocessingDocument package = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document))
        {
            MainDocumentPart mainPart = package.AddMainDocumentPart();
            using (Stream stream = mainPart.GetStream())
            {
                byte[] buf = (new UTF8Encoding()).GetBytes(request.XmlString);
                stream.Write(buf, 0, buf.Length);
            }
            package.Save();
        }

its Generate file and i can return as File And IActionResult Like Below:

return File(mem.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "document.docx");
Related