Ниже приведен код, который я использую для создания нового документа Word из существующего документа. Что работает, так это то, что он успешно читает документ шаблона (templateName) и может создать строку customXML из класса модели. Но то, что не работает, - это информация customXML (адрес, город, состояние, zip,...) не заменяет пустые теги.
Я вытащил этот код из старого.NET-приложения.NET, и я использую его в MVC 4.5 с OpenXml 2.5. Старая версия кода работает, но не в версии MVC.
public void CreateDocument(Customer customer, string templateName, string documentPath)
{
// Need to create an in memory resizable byte array for loading the county template
byte[] byteArray = File.ReadAllBytes(templateName);
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
// Open the in memory Open XML document for processing
//using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(templateName, true))
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true))
{
// Build XML file from the Data Class that will be populated from data entered
// into the Add Customer screen.
// THIS CODE WORKS - A VALID XML STRING IS CREATED
string customXML = BuildCustomXmlPartFromDataClass(customer);
// Replace Custom XML portion of Open XML Package document
// THE mainPart.CustomXmlParts IS NOT NULL
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
if (mainPart.CustomXmlParts != null)
{
mainPart.DeleteParts<CustomXmlPart>(mainPart.CustomXmlParts);
}
CustomXmlPart customXmlPart = mainPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
using (StreamWriter ts = new StreamWriter(customXmlPart.GetStream())) ts.Write(customXML);
}
// Save the in memory byte array to disk using a FileStream
using (FileStream fileStream = new FileStream(documentPath, System.IO.FileMode.Create))
{
mem.WriteTo(fileStream);
}
// NEW DOCUMENT IS CREATED BUT IT DOES NOT CONTAIN ANY OF THE INFORMATION FROM THE MODEL CLASS
}
}
protected string BuildCustomXmlPartFromDataClass(Customer customer)
{
// Serialize the Data Class to XML
System.Xml.XmlDocument doc = new XmlDocument();
System.Xml.Serialization.XmlSerializer serializer =
new System.Xml.Serialization.XmlSerializercustomer.GetType());
System.IO.MemoryStream stream = new System.IO.MemoryStream();
try
{
serializer.Serialize(stream, customer);
stream.Position = 0;
doc.Load(stream);
return doc.InnerXml;
}
catch
{
throw;
}
finally
{
stream.Close();
stream.Dispose();
}
}
Ошибок нет и создается новый документ, но он не содержит информации из класса модели (клиента). Я видел похожие сообщения, но решение не работало.
Во-первых, нам, вероятно, нужна дополнительная информация:
Вы можете проверить XML, хранящиеся в документе, различными способами:
Я думаю, а не DeleteParts<CustomXmlPart>(mainPart.CustomXmlParts)
вы должны выборочно заменить часть, которую вы ищете. Вы можете (должны) использовать xmlns корневого элемента для идентификации частей (или вы можете проверить имя корневого элемента, если ваша часть не имеет xmlns, и вы также не можете добавить ее).
Этот код демонстрирует, как построить XML с корневым пространством имен (что означает, что все дети также должны иметь это пространство имен). Вам нужно будет исследовать, как указать пространства имен через атрибуты (или реализовать IXmlSerializable):
XDocument GetXDocument()
{
var xmlns = (XNamespace)"http://schemas.tempuri.org/product/v1/wordMetadata";
var childElement =
new XElement(xmlns + "childElement",
this.ChildCollection.Select(x => x.GetXElement(xmlns, "grandChildElement")));
var rValue = new XDocument(
new XElement(xmlns + "root",
childElement
));
return rValue;
}
public XElement GetXElement(XNamespace xmlns, string elementName)
{
return new XElement(xmlns + elementName);
}
Код ниже заменит XML-часть документа, которая имеет такое же пространство имен, что и входящий XDocument
.
public static class WriteWord
{
public static MemoryStream BuildFile(string templatePath, XDocument xmlData)
{
MemoryStream rValue = null;
byte[] fileBytes;
fileBytes = File.ReadAllBytes(templatePath);
rValue = BuildFile(fileBytes, xmlData);
return rValue;
}
public static MemoryStream BuildFile(byte[] fileBytes, XDocument xmlData)
{
var rValue = new MemoryStream();
var reader = xmlData.CreateReader();
reader.MoveToContent();
var xmlNamespace = reader.NamespaceURI; // "http://schemas.tempuri.org/product/v1/wordMetadata";
rValue.Write(fileBytes, 0, fileBytes.Length);
var openSettings = new OpenSettings()
{
AutoSave = true,
//MarkupCompatibilityProcessSettings =
// new MarkupCompatibilityProcessSettings(
// MarkupCompatibilityProcessMode.ProcessAllParts,
// FileFormatVersions.Office2013)
};
using (WordprocessingDocument doc = WordprocessingDocument.Open(rValue, true, openSettings))
{
MainDocumentPart main = doc.MainDocumentPart;
var mainPart = doc.MainDocumentPart;
var xmlParts = mainPart.CustomXmlParts;
var ourPart = (CustomXmlPart)null;
foreach (var xmlPart in xmlParts)
{
var exists = false;
using (XmlTextReader xReader = new XmlTextReader(xmlPart.GetStream(FileMode.Open, FileAccess.Read)))
{
xReader.MoveToContent();
exists = xReader.NamespaceURI.Equals(xmlNamespace);
}
if (exists)
{
ourPart = xmlPart;
break;
}
}
using (var xmlMS = new MemoryStream())
{
xmlData.Save(xmlMS);
xmlMS.Position = 0;
ourPart.FeedData(xmlMS);
}
}
rValue.Position = 0;
return rValue;
}
}