I have a class, created from Agresso's XSD using xsd.exe, which I successfully use to generate my XML file. I define an object ABWInvoice oInvoiceAgresso = new ABWInvoice() { }; , populate with relevant data and then save as an XML file, using the following method.
public static void SaveXml<ObjectType>(ObjectType o, string fileName)
{
using (var sw = new StreamWriter(myDecodePath(fileName)))
{
new XmlSerializer(typeof(ObjectType)).Serialize(sw, o);
}
}
The XML file has lots of namespaces, e.g.
<InvoiceDate xmlns="http://services.agresso.com/schema/ABWSchemaLib/2011/11/14">2018-02-05</InvoiceDate>
So far all well, but a customer now requires to remove the namespaces across the XML, i.e. to present only <InvoiceDate>2018-02-05</InvoiceDate>
There are lots of examples on the Net how to do it with an XML file, meaning I will have to continue saving my object as an XML file and then to reload it for processing.
I wonder if there is any better and I think also faster approach to do it directly on my oInvoiceAgresso object, rather than saving it first, please?
We use VS 2017.
Related
I'm new to programming in C#. I want to create and download an xml file. I found this artical on creating the xml. I followed it and it works perfect. But I can't figure out how to save the file to my computer. I think it has to be inplemented someware here:
public static void Main()
{
// Read and write purchase orders.
Test t = new Test();
t.CreatePO("po.xml");
//I think here the file is ready to dowload
t.ReadPO("po.xml");
}
As for the t.CreatePO("po.xml"); function I have exactly whats in the artical.
From the artical I took the last example.
The 'file' is created by a StreamWriter. Then it converts an object to a XML by using Serialize.
Any step in the right direction will help!
For your question, you want to save the file to your computer.
You could try the following code to get it.
// Creates an instance of the XmlSerializer class;
// specifies the type of object to serialize.
XmlSerializer serializer =
new XmlSerializer(typeof(PurchaseOrder));
//We can use absolute paths to store it anywhere on the computer
string xmlPath = #"D:\Task\";
TextWriter writer = new StreamWriter(Path.Combine(xmlPath,filename));
PurchaseOrder po = new PurchaseOrder();
Result:
I'm building a class library in C# which uses the XmlTextWriter class to build an XML which is then exported as a HTML document.
However when I save the file with a .html extension using the XmlTextWriter object as content, the resulting file only contains the text "System.Xml.XmlTextWriter"
This comes up in the method defined below, specifically in the final line:-
public void SaveAsHTML(string filepath)
{
XmlTextWriter html;
html = new XmlTextWriter(#"D:/HTMLWriter/XML/HTMLSaveAsConfig.xml", System.Text.Encoding.UTF8);
html.WriteStartDocument();
html.WriteStartElement("html");
html.WriteRaw(Convert.ToString(Head));
html.WriteRaw(Convert.ToString(Body));
html.WriteEndElement();
html.WriteEndDocument();
html.Flush();
html.Close();
System.IO.File.WriteAllText(filepath, html.ToString());
}
For context, the variables Head and Body are also XmlTextWriter objects containing what will become the and elements of the html file respectively.
I've tried using Convert.ToString(), which causes the same issue.
I'm tempted to try overriding the ToString() method for my class as a fix, potentially using the XmlSerializer class. However I was wondering if there's a less noisy way of returning the Xml object as a string?
The following function will extract the string from the System.Xml.XmlTextWriter objects you've created as Head and Body.
private string XmlTextWriterToString(XmlTextWriter writer)
{
// Ensure underlying stream is flushed.
writer.Flush();
// Reset position to beginning of stream.
writer.BaseStream.Position = 0;
using (var reader = new StreamReader(writer.BaseStream))
{
// Read and return content of stream as a single string
var result = reader.ReadToEnd();
return result;
}
}
Some caveats here are that the underlying System.IO.Stream object associated with the System.Xml.XmlTextWriter must support both 'read' and 'seek' operations (i.e., both Stream.CanRead and System.CanSeek properties must return true, respectively).
I've made the following edits to your original code:
Replaced the Convert.ToString() calls with calls to this new function.
Made an assumption that you're intending to write to the file specified by the filepath parameter to your SaveAsHTML() function, and not the hard-coded path.
Wrapped the creation (and use, and disposal) of the System.Xml.XmlTextWriter in a using block (if you're not familiar, see What are the uses of “using” in C#?).
Following is your code with those changes.
public void SaveAsHTML(string filepath)
{
using (var html = new XmlTextWriter(filepath, System.Text.Encoding.UTF8))
{
html.WriteStartDocument();
html.WriteStartElement("html");
html.WriteRaw(XmlTextWriterToString(Head));
html.WriteRaw(XmlTextWriterToString(Body));
html.WriteEndElement();
html.WriteEndDocument();
html.Flush();
html.Close();
}
}
Another thing of which to be mindful is that, not knowing for sure from the code provided how they're being managed, the lifetimes of Head and Body are subject to the same exception-based resource leak potential that html was before wrapping it in the using block.
A final thought: the page for System.Xml.XmlTextWriter notes the following: Starting with the .NET Framework 2.0, we recommend that you create XmlWriter instances by using the XmlWriter.Create method and the XmlWriterSettings class to take advantage of new functionality.
The last line writes the value of XmlTextWriter.ToString(), which does not return the text representation of the XML you wrote. Try leaving off the last line, it looks like your XmlTextWriter is already writing to a file.
#PhilBrubaker's solution seems to be on the right track. There are still a few bugs in my code that I'm working towards getting a fix for, but the good news is that the casting seems to be working now.
protected string XmlToString(XmlWriter xmlBody)
{
XmlTextWriter textXmlBody = (XmlTextWriter)xmlBody;
textxmlBody.BaseStream.Position = 0;
using (var reader = new StreamReader(textXmlBody.BaseStream))
{
var result = reader.ReadToEnd();
reader.Dispose();
return result;
}
}
I've changed the input parameter type from XmlWriter and cast it explicitly to XmlTextWriter in the method, this is so that the method also works when the Create() method is used instead of an initialisation as recommended for .NET 2.0. It's not 100% reliable at the moment as XmlWriter doesn't always cast correctly to XmlTextWriter (depending on the features), but that's out of the scope for this thread and I'm investigating that separately.
Thanks for your help!
On a side note, the using block is something I haven't come across before, but it's provided so many solutions across the board for me. So thanks for that too!
I am using http://www.thescarms.com/dotnet/XSLT.aspx to Convert comma delimited data (CSV) to XML using XSLT template.
It uses the foll. 2 lines of .NET code:
XSLT.Load(mstrInputXSLTFile, resolver);
XSLT.Transform(mstrInputCSVFile, mstrOutputXMLFile, resolver);
I am looking for a way in which I can use the string contents (contents of the XSLT, CSV file) instead of files in above 2 methods.. Any help will be usefull.
I am planning to implement this logic in a WCF webservice which will receive the csv string. If there is no workaround then I will have to create temp files based on the values of csv and xsl received. Process the conversion of csv to xml on the server and return the xml output to the client. Then delete the files created above.
If you want to load the input from a string then create an XmlReader over a StringReader over your string e.g.
XslCompiledTransform proc = new XslCompiledTransform();
using (StringReader sr = new StringReader(stringVar))
{
using (XmlReader xr = XmlReader.Create(sr))
{
proc.Load(xr);
}
}
There is no suitable method in XslTransform which does what you want.
However, you could write your own extensions methods (I would call them Parse..), which take content as string, create files in the temporary directory, and load/transform them by the suitable methods.
I found this great tutorial for loading XML using XLINQ (LINQ to XML).
http://www.codearsenal.net/2012/07/c-sharp-load-xml-using-xlinq.html
It helped me a lot and I got the job done with it.
The only change I made was where he had this line:
from e in XDocument.Load(#"..\..\Employees.xml").Root.Elements("employee")
I write it like this:
from el in XDocument.Load("XML_Files/Employees.xml").Root.Elements("employee")
I had to change the path like this to access a local xml file found right inside my Visual Studio project.
But now I need to save the data back to the file in my project solution. Again, my xml file is located inside my C# project. It's not on the desktop or anything, it is a file added to the project solution.
I can't seem to find any good resources for how to do this task. Does anyone know a good tutorial, or code, a reference to start?
I am inserting a list of objects into the xml files. The objects have basic data type properties, except for one of the object properties, which is a List of doubles.
Can anyone advise a good tutorial or link? Or even a generic code sample?
I'd like to keep this function as basic as possible.
Please help.
------------------ UPDATE ------------------
I actually got this kind of working now. The below code does what I need EXCEPT that it won't write the data to my local file in the Visual Studio project. It will gladly write the data to a test file I created on my desktop, however.
Does anyone know why this is??
//create the serialiser to create the xml
XmlSerializer serialiser = new XmlSerializer(typeof(List<Student>));
// Create the TextWriter for the serialiser to use
TextWriter Filestream = new StreamWriter(#"C:\\Users\\MyName\\Desktop\\output.xml");
//write to the file
serialiser.Serialize(Filestream, employees);
// Close the file
Filestream.Close();
-------- UPDATE ---------
Okay, figured it out.
This code works:
public void WriteXML()
{
//create the serialiser to create the xml
XmlSerializer serialiser = new XmlSerializer(typeof(List<Student>));
// Create the TextWriter for the serialiser to use
TextWriter Filestream = new StreamWriter(#"XML_Files\Employees.xml");
//write to the file
serialiser.Serialize(Filestream, employees);
// Close the file
Filestream.Close();
}
The data is inserted to the xml file, but it does not show in Visual Studio. But when I checked here:
C:\Users\Me\Desktop\MyProject\MyProject\bin\Debug\XML_Files
The file is overwritten.
Also, when I reload the data from the application again, the new entries come up.
The problem is in line:
TextWriter Filestream = new StreamWriter(#"C:\\Users\\MyName\\Desktop\\output.xml");
Change it to one of following:
TextWriter Filestream = new StreamWriter("C:\\Users\\MyName\\Desktop\\output.xml");
TextWriter Filestream = new StreamWriter(#"C:\Users\MyName\Desktop\output.xml");
Simply remove the "#", OR use single slashes:
So far I've been able to set custom properties to a Word doc by using VSTO and by adding a package stream to the active document as it follows
public static void SetCustomProperty(Microsoft.Office.Interop.Word.Document doc, string propertyName, object propertyValue)
{
using (MemoryStream stream = new MemoryStream())
using ((WordprocessingDocument wordDoc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document, true))
{
SetProperty(wordDoc, propertyName, propertyValue);
// Flush the contents of the package.
wordDoc.Package.Flush();
// Convert back to flat OPC by using this in-memory package.
XDocument xDoc = OpcHelper.OpcToFlatOpc(wordDoc.Package);
// Return the xml string.
string openxml = xDoc.ToString();
// Add to Word doc
doc.CustomXMLParts.Add(openxml);
}
}
The SetProperty method works as explained here and the OpcHelper can be found here and is explained here.
The problem is that my custom property is inserted in a xml file (e.g. item1.xml) that is located in the folder document.zip\customXml of the OpenXML file format. Later on when I want to read my custom property I use the WordProcessingDocument.CustomFilePropertiesPart which is empty. In fact I found that CustomFilePropertiesPart references the document.zip\docProps\custom.xml file.
So instead of using doc.CustomXMLParts.Add(openxml); what should I use to populate the right xml file, i.e. document.zip\docProps\custom.xml?
EDIT
I tried already the solution proposed by Mishra without success, i.e custom properties were not always saved. However since he posted this solution I tried again and I found here that you firstly need to mark the document as unsaved:
doc.CustomDocumentProperties.Add("MyProp", False, MsoDocProperties.msoPropertyTypeNumber, 123);
doc.Saved = false;
doc.Save();
you cant set custome properties using CustomXMLParts collection. If you have document open better keep it simple and use CustomDocumentProperties collection, its quite fast and easy. I would use open XML in open doc only if the data to insert is vary large.