Error in my XML? - c#

I have the following code:
public class DeserializeAndCompare
{
public static List<string> IntoXML()
{
List<string> PopList = new List<string>();
XmlSerializer serializer = new XmlSerializer(PopList.GetType());
string k = FileToolBox.position0;
FileStream filestreamer = new FileStream(k.ToString(), FileMode.Open);
PopList = (List<string>)serializer.Deserialize(filestreamer);
filestreamer.Close();
return PopList;
}
}
I keep hitting an error with the line:
PopList = (List)serializer.Deserialize(filestreamer);
The error: InvalidOperationException was unhandled, There is an error in XML document(1,1).
In this line:
FileStream filestreamer = new FileStream(k, FileMode.open);
I am trying to reference the 0th position of an array that holds strings. I'm basically going thru my directory, finding any files with a .xml extension and holding the filename paths in an array.
Here is the code for my array:
public static class FileToolBox
{
public static string position0;
public static void FileSearch()
{
//string position0;
//array holding XML file names
string[] array1 = Directory.GetFiles(#"s:\project", "*.xml");
Array.Sort(array1);
Array.Reverse(array1);
Console.WriteLine("Files:");
foreach (string fileName in array1)
{
Console.WriteLine(fileName);
}
position0 = array1[0];
}
public static string Position0
{
get
{
return position0;
}
set
{
position0 = value;
}
}
}
Am i missing something here? How do i get rid of this error?
Thanks in advance for the help.

Your XML file is not well formed, use a tool like XML Spy, XML notepad or open it up in IE and it will give you the error and the line it is on. You most likely have invalid characters like & somewhere in the file

That error is specifically indicating that the XML file being read is malformed. You should start by posting your XML. Also, try opening the XML in Firefox, because it may point out the problem with the XML as well.

Your xml document is not well formed, you need to open your xml file up and analyze it.
There are multiple xml validators on the web, but here's one from w3schools.

Give this a shot:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace Util
{
/// <summary>
/// Not to be confused with System.Xml.Serialization.XmlSerializer, which this uses internally.
///
/// This will convert the public fields and properties of any object to and from an XML string,
/// unless they are marked with NonSerialized() and XmlIgnore() attributes.
/// </summary>
public class XMLSerializer
{
public static Byte[] GetByteArrayFromEncoding(Encoding encoding, string xmlString)
{
return encoding.GetBytes(xmlString);
}
public static String SerializeToXML<T>(T objectToSerialize)
{
return SerializeToXML(objectToSerialize, Encoding.UTF8);
}
public static String SerializeToXML<T>(T objectToSerialize, Encoding encoding)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings =
new XmlWriterSettings { Encoding = encoding, Indent = true };
using (XmlWriter xmlWriter = XmlWriter.Create(sb, settings))
{
if (xmlWriter != null)
{
new XmlSerializer(typeof (T)).Serialize(xmlWriter, objectToSerialize);
}
}
return sb.ToString();
}
public static void DeserializeFromXML<T>(string xmlString, out T deserializedObject) where T : class
{
DeserializeFromXML(xmlString, new UTF8Encoding(), out deserializedObject);
}
public static void DeserializeFromXML<T>(string xmlString, Encoding encoding, out T deserializedObject) where T : class
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (MemoryStream memoryStream = new MemoryStream(GetByteArrayFromEncoding(encoding, xmlString)))
{
deserializedObject = xs.Deserialize(memoryStream) as T;
}
}
}
}
public static void Main()
{
List<string> PopList = new List<string>{"asdfasdfasdflj", "asdflkjasdflkjasdf", "bljkzxcoiuv", "qweoiuslfj"};
string xmlString = Util.XMLSerializer.SerializeToXML(PopList);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
string fileName = #"C:\temp\test.xml";
xmlDoc.Save(fileName);
string xmlTextFromFile = File.ReadAllText(fileName);
List<string> ListFromFile;
Util.XMLSerializer.DeserializeFromXML(xmlTextFromFile, Encoding.Unicode, out ListFromFile);
foreach(string s in ListFromFile)
{
Console.WriteLine(s);
}
}
Check the output XML file and see what the encoding is, and compare that to what encoding you're trying to read in. I got caught with this problem before because I use a StringBuilder to output the XML string, which writes in UTF-16, but I was trying to read in as UTF-8. Try using Encoding.Unicode and see if that works for you.

Your code will only work with XML files which have the following structure...
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>World</string>
</ArrayOfString>

Related

C# append object to xml file using serialization

I am trying append a serialized object to an existing xml file beneath the root element, which I thought would be simple but is proving to be a little challenging.
The problem is in the AddShortcut method but I added some more code for completeness.
I believe what I need to do is:
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument.
The trouble is in steps 2 and 3. I have tried different variations but I think using XPathNavigator somehow to find the "root" node to append under is a step in the right direction.
I have also looked at almost every question on Stack Overflow on the subject.
Any suggestions welcome. Here is my code
class XmlEngine
{
public string FullPath { get; set; } // the full path to the xmlDocument
private readonly XmlDocument xDoc;
public XmlEngine(string fullPath, string startElement, string[] rElements)
{
FullPath = fullPath;
xDoc = new XmlDocument();
CreateXmlFile(FullPath, startElement, rElements);
}
public void CreateXmlFile(string path, string startElement, string[] rElements)
{
try
{
if (!File.Exists(path))
{
// create a txt writer
XmlTextWriter wtr = new XmlTextWriter(path, System.Text.Encoding.UTF8);
// make sure the file is well formatted
wtr.Formatting = Formatting.Indented;
wtr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
wtr.WriteStartElement(startElement);
wtr.Close();
// write the top level root elements
writeRootElements(path, rElements);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.WriteLine("Could not create file: " + path);
}
}
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
rootNode = xDoc.AppendChild(xDoc.CreateElement("Shortcuts"));
var serializer = new XmlSerializer(s.GetType());
using (var writer = new StreamWriter(FullPath, true))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.OmitXmlDeclaration = true;
serializer.Serialize(writer, s);
}
xDoc.Save(FullPath);
}
}
This code sample worked for me:
xml:
<?xml version="1.0" encoding="UTF-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\SomePath</FullPath>
<Name>SomeFolderName</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
Method:
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
var rootNode = xDoc.GetElementsByTagName("Shortcuts")[0];
var nav = rootNode.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(s.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, s, emptyNamepsaces);
writer.Close();
}
xDoc.Save(FullPath);
}
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument
So:
public void AddShortcut(Shortcut s)
{
// 1. load existing xml
xDoc.Load(FullPath);
// 2. create an XML node from object
XmlElement node = SerializeToXmlElement(s);
// 3. append that node to Shortcuts node under XML root
var shortcutsNode = xDoc.CreateElement("Shortcuts")
shortcutsNode.AppendChild(node);
xDoc.DocumentElement.AppendChild(shortcutsNode);
// 4. save changes
xDoc.Save(FullPath);
}
public static XmlElement SerializeToXmlElement(object o)
{
XmlDocument doc = new XmlDocument();
using(XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
This post

xmlwriter , it's being used by another process

Here, i created a class, and what i am trying to accomplish is to write the contents from the list into an xml file.
1) At first run, it creates the file and trows an error here: Token EndElement in state EndRootElement would result in an invalid XML document
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//second run, error Occurr here
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//first Run error is thrown in here.
xmlOut.WriteEndElement();
xmlOut.Close();
}
2) When i rerun on the second time, the error is in same method.
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
xmlOut.WriteEndElement();
xmlOut.Close();
}
The whole class i here:
class ProductCellPhoneDB
{
private const string path = #"..\..\CellPhoneProducts.xml";
public static List<ProducCellPhone> GetCellPhoneProducts()
{
List<ProducCellPhone> localCellPhoneProducts =
new List<ProducCellPhone>();
XmlReaderSettings localSettings = new XmlReaderSettings();
localSettings.IgnoreWhitespace = true;
localSettings.IgnoreComments = true;
XmlReader xmlIn = (XmlReader.Create(path,localSettings));
if (xmlIn.ReadToDescendant("Cell"))
{
do
{
ProducCellPhone localProduct = null;
xmlIn.ReadStartElement("Cell");
localCellPhoneProducts.Add(localProduct);
}
while (xmlIn.ReadToNextSibling("Cell"));
}
xmlIn.Close();
return localCellPhoneProducts;
}
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//Error Occurr here
//xml writer object, CellPhoneProduct, error is being used by other process?
XmlWriter xmlOut = (XmlWriter.Create(path, localSettings));
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//ERROR Token EndElement in state EndRootElement would result in an invalid XML document
xmlOut.WriteEndElement();
xmlOut.Close();
}
private static void ReadCellphoneProductBase(XmlReader xmlIn, ProducCellPhone localProduct)
{
localProduct.Iemi = xmlIn.ReadElementContentAsString();
localProduct.Model = xmlIn.ReadContentAsString();
localProduct.Price = xmlIn.ReadContentAsDecimal();
}
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
}
Any suggestions would be helpful. Thanks community. !
The first error
you get is likely because the WriteStartElement and WriteEndElement calls are not matched. You do xmlOut.WriteStartElement("Cell"); once, but do xmlout.WriteEndElement(); several times, once for each ProducCellPhone in LocalProducts, plus another time after the foreach.
To solve this (if I guessed your XML document structure right), you should change your WriteCellPhoneProductBase method to:
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlOut.WriteStartElement("Cell");
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
And remove the WriteStartElement and WriteEndElement lines from SaveCellPhoneProducts (see below).
The second error is probably because the XmlWriter used when you got the first error was not disposed and has not closed the file handle. You should always use a using block to ensure IDisposable resources get disposed, also when an exception occurs. You should change your method to:
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
//xml writer settings
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
using (var xmlOut = XmlWriter.Create(path, localSettings))
{
xmlOut.WriteStartDocument();
//write each product on xml file
foreach(ProducCellPhone localProduct in LocalProducts)
WriteCellPhoneProductBase(localProduct, xmlOut);
xmlOut.WriteEndDocument()
}
}
For your GetCellPhoneProducts follow the same using block approach.

Convert Multiple XML files into single CSV using XSLT and C#

I'm trying to convert XML file to CSV using XSLT. It works. I was able to convert XML and get the output I want using XSLT.
The challenge I'm facing right now is that I have many many XML files in a single location. I want to get ALL the data from all XMLs and put them into a single CSV file. I have a for loop that goes through the folder and gets the XML files and then exports it to CSV. However, every time it converts new XML it overrides the data in the current CSV file. So the end result is I only get one row in the CSV file instead of 500 (if there's 500 xml files).
Here's the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.IO;
using System.Diagnostics;
namespace XSL
{
class Program
{
public static void Main(string[] args)
{
try
{
//declaring xmlFile
string xmlfile ;
//Loading the XSLT template
String xsltfile = "C:\\Win\\XMLReader\\XSL\\csv.xsl";
//get folder location
string d = DateTime.Now.ToString("yyyyMMdd");
//Console.WriteLine(d.ToString());
//first part of the location path
String firstPath = #"\\tripx-exportm\\output\\Skill 53115;1_";
//full path
string fullPath = firstPath + d.ToString() + "_000000" + #"\\IDX";
//Get files from a folder
string[] filePath = Directory.GetFiles(fullPath, "*.xml");
//get each file in the folder
foreach (string file in filePath)
{
Console.WriteLine(file);
xmlfile = file;
Transform(xmlfile, xsltfile);
}
//Get the count of XML files in the current folder
DirectoryInfo dir = new DirectoryInfo(fullPath);
int count = dir.GetFiles().Length;
Console.WriteLine("Count of XML files: " + count);
//Transform(xmlfile, xsltfile);
Console.WriteLine("press any key");
Console.ReadKey();
}
catch(Exception e){
Console.WriteLine(e.ToString());
}
}
public static void Transform(string xml, string xslt)
{
try
{
//load the xml doc
XPathDocument myDoc = new XPathDocument(xml);
XslCompiledTransform myXL = new XslCompiledTransform();
//load the xslt doc
myXL.Load(xslt);
//create the output
XmlTextWriter myWriter = new XmlTextWriter("result.csv", null);
myXL.Transform(myDoc, null, myWriter);
myWriter.Close();
}
catch (Exception e)
{
Console.WriteLine("exception : {0}", e.ToString());
};
}
}
}
XSLT file
Any suggestions on how I can put the data from multiple XMLs into a single CSV?
Thank you
You need to use the right constructor overload for the XmTextWriter to take a stream, rather than a file name.
The help on MSDN says this about the file name overload:
The filename to write to. If the file exists, it truncates it and
overwrites it with the new content.
So you should to do this to Transform to make it work:
public static void Transform(Stream stream, string xml, string xslt)
{
var myDoc = new XPathDocument(xml);
var myXL = new XslCompiledTransform();
myXL.Load(xslt);
using(var myWriter = new XmlTextWriter(stream, Encoding.Default))
{
myXL.Transform(myDoc, null, myWriter);
myWriter.Flush();
myWriter.Close();
}
}
Then the calling code should look like this:
using (var fs = new FileStream("result.csv", FileMode.Create))
{
foreach (string file in filePath)
{
Transform(fs, file, xsltfile);
}
fs.Flush();
fs.Close();
}
I've taken out the (bad) exception handling, and comments, etc.
I have come up with a different approach. It may not be the best possible solution, but it works for me.
In my code, I added a counter to count the number of XMLs that were processed. It increments.
In my Transform procedure instead of hard coding the name "result.csv", I generate the name:
string Result = "result" + count.ToString() + ".csv";
And I use that name in the XMLWriter
XmlTextWriter myWriter = new XmlTextWriter(Result, null);
This way it generates a single CSV for every XML and it will not override the existing one.
Then I wrote another procedure that combines all the CVSs into one:
private static void JoinCsvFiles(string[] csvFileNames, string outputDestinationPath)
{
StringBuilder sb = new StringBuilder();
bool columnHeadersRead = false;
foreach (string csvFileName in csvFileNames)
{
TextReader tr = new StreamReader(csvFileName);
string columnHeaders = tr.ReadLine();
// Skip appending column headers if already appended
if (!columnHeadersRead)
{
sb.AppendLine(columnHeaders);
columnHeadersRead = true;
}
sb.AppendLine(tr.ReadToEnd());
}
File.WriteAllText(outputDestinationPath, sb.ToString());
}
And in the main, I just call
string[] csvFileNames = Directory.GetFiles(".", "result*.csv");
JoinCsvFiles(csvFileNames, "CsvOutput.csv");
Hope this helps somebody.

Root element is missing in microsoft xml file

I try to open xml file that I created like this:
public List<MyClass> OpenFile(string path)
{
try
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<MyClass>));
TextReader textReader = new StreamReader(path);
List<MyClass> _newList = (List<MyClass>)deserializer.Deserialize(textReader);
textReader.Close();
retrun _newList ;
}
catch (Exception e)
{
return null;
}
}
I get the error: Root element is missing.
I saw several places that error appears because the line: <? Xml version = "1.0"?> Missing.
I do not understand why it did not work for me, I created the file using the microsoft xml (it worked for me in the past, I added a int variable to my class and then everything went wrong):
public void SaveFile(string path,List<MyClass> list)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(List<MyClass>));
TextWriter textWriter = new StreamWriter(path);
serializer.Serialize(textWriter,list);
textWriter.Close();
}
catch (Exception e)
{
}
}
What's the problem?
And to everyone who asks himself, then yes I have this line in the file!

XML Deserializing a List<>

I have managed to serialize a list of objects of type Word using XML Serialization:
public static void WriteXML(string fileName)
{
System.Xml.Serialization.XmlSerializer writer =
new System.Xml.Serialization.XmlSerializer(typeof(Word));
System.IO.StreamWriter file = new System.IO.StreamWriter(
fileName);
foreach (var word in Words)
{
writer.Serialize(file, word);
}
file.Close();
}
I have a problem with deserializing this list. Im using this code snippet: http://msdn.microsoft.com/en-us/library/vstudio/ms172872.aspx
I changed my code to something like that:
public static void ReadXML(string fileName)
{
System.Xml.Serialization.XmlSerializer reader =
new System.Xml.Serialization.XmlSerializer(typeof(Word));
System.IO.StreamReader file = new System.IO.StreamReader(
fileName);
foreach (????)
{
Word word=new Word();
word = (Word) reader.Deserialize(file);
Words.Add(word); //Words is a List<Word>
}
}
Of course the foreach() loop is not used properly here. I just have no clue how to do this.
First of all, you should not serialize each word one by one. This would result in a single file containing many xmls, which would of course be invalid.
You want to serialize Words (which is List<Word>) . Therefore your serializer creation should be new XmlSerializer(typeof(List<Word>)) and serialization as writer.Serialize(file, Words);
So your code can be like this:
List<Word> Words = ........
WriteXML("a.xml", Words);
var newWords = ReadXML<List<Word>>("a.xml");
public static void WriteXML(string fileName,object obj)
{
using (var f = File.Create(fileName))
{
XmlSerializer ser = new XmlSerializer(obj.GetType());
ser.Serialize(f, obj);
}
}
public static T ReadXML<T>(string fileName)
{
using (var f = File.Open(fileName,FileMode.Open,FileAccess.Read))
{
XmlSerializer ser = new XmlSerializer(typeof(T));
return (T)ser.Deserialize(f);
}
}
PS: Serializable attribute is required only for BinaryFormatter. XmlSerializer doesn't need it.
You can find the details of the attributes XmlSerializer uses here
How can you serialize individual Word object to same file? This is a kind of overriding the file on each iteration. Simply just serialize the Database object instead of separate Word objects this way:
System.Xml.Serialization.XmlSerializer writer =
new System.Xml.Serialization.XmlSerializer(typeof(Database));
System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
writer.Serialize(file, yourDatabaseObject);
Note: In addition, make sure that Database is marked with Serializable attribute.

Categories