Deleting element from Xml breaks format on reload - c#

I am creating a system that stores vehicle data. When I serialize the data using Xml serialization, I get the correct format as shown in the example below:
<?xml version="1.0"?>
<ArrayOfVehicle xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Vehicle>
<Registration>fake1</Registration>
<Model>123</Model>
<Make>test</Make>
<Year>1999</Year>
<Cost>100</Cost>
</Vehicle>
<Vehicle>
<Registration>fake2</Registration>
<Model>321</Model>
<Make>123</Make>
<Year>2000</Year>
<Cost>321</Cost>
</Vehicle>
</ArrayOfVehicle>
The serialization uses a list of vehicles that have the attributes seen in the Xml file. I am trying to figure out how I can delete a vehicle from the list and serialize it back to the Xml file without breaking the format shown above.
The method that I have tried to use to delete the records from the list and serialize and deserialize the data, but when I remove and item, it breaks the format. This is what the Xml file looks like when I remove an item from the list and serialize it:
fake1 123 test 1999 100
Here is my code for removing an item:
for (int i = Business.VehicleList.Count - 1; i >= 0; i--)
{ //Where Business.VehicleList is my list
if (Business.VehicleList[i].Registration == registration)
{
Business.VehicleList.RemoveAt(i);
Business.Save(); //Method for serialization
}
}
Here is the error it throws when I try to deserialize the data again:
System.InvalidOperationException: 'There is an error in XML document (10, 19). XmlException: There are multiple root elements. Line 10, position 19.'
These are my serialization and deserialization methods:
public static void Retrieve()
{
using (FileStream fileStream = new FileStream("C:\\temp\\data.xml", FileMode.OpenOrCreate))
{
using (var reader = new StreamReader(fileStream))
{
if (fileStream.Length <= 0)
{
return;
}
else
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Vehicle>),
new XmlRootAttribute("ArrayOfVehicle"));
_vehicleList = (List<Vehicle>)deserializer.Deserialize(reader); //This is where the error is thrown
}
}
}
}
public static void Save()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Vehicle>));
using (FileStream fileStream = new FileStream("C:\\temp\\data.xml", FileMode.Open))
{
serializer.Serialize(fileStream, VehicleList);
fileStream.Close();
}
}
Any suggestions on how to remove a vehicle from my list without it breaking the Xml file?
Here is the source after I tried deleting an item from the vehicle string
<?xml version="1.0"?>
<ArrayOfVehicle xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Vehicle>
<Registration>123</Registration>
<Model>123</Model>
<Make>23</Make>
<Year>2000</Year>
<Cost>123</Cost>
</Vehicle>
</ArrayOfVehicle><Registration>1321</Registration>
<Model>123123</Model>
<Make>312312</Make>
<Year>2000</Year>
<Cost>321</Cost>
</Vehicle>
</ArrayOfVehicle>

In the Save method, new FileStream("C:\\temp\\data.xml", FileMode.Open) will open the existing file without truncating it. So after you write the new XML data to the file, there will be remnants of the old content if the new content is shorter than the old one.
Changing this to new FileStream("C:\\temp\\data.xml", FileMode.Create) will fix the issue.

I think it's because you are trying to de-serialize a malformed xml. Please first, make sure that your serialization method produces correct xml. The reason may be because of closing the stream inside using statement. And also serializing the list before for-loop finishes.
Try removing fileStream.Close(); and also moving Business.Save(); to outside of for-loop.
Here, I made a fiddle with same conditions and it works.

Related

C# Trouble looping through elements in XML stream

I have a URL that outputs XML when accessed and I want to read that XML as a stream (I think) and loop over the elements to do something with the data, but I'm getting strange results.
My code so far:
private void getFaxFinderLogs()
{
WebRequest request = WebRequest.Create("https://faxfinder/ffws/v1/inbound_log");
request.Credentials = new NetworkCredential(ffusername_tb.Text, ffpassword_tb.Text);
using (WebResponse response = request.GetResponse())
using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
{
var xdoc = XDocument.Load(reader);
foreach (var el in xdoc.Elements())
{
debugTextBox1.Text = string.Format("Key={0},value={1}", el.Name, el.Value);
}
}
}
And the XML from the URL above produces this structure:
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<message>Success</message>
<inbound_fax_entry>
<filename>fax_inbound_20170313_133831_recv0943.pdf</filename>
<status>complete</status>
<complete_time>2017-03-13T17:38:31</complete_time>
<remote_id>5551239999</remote_id>
<pages>2</pages>
<extension>3555</extension>
<recipient_name>Customer Service</recipient_name>
<channel>8</channel>
<delivered_to>
<type>Share</type>
<destination>//fileserver/share/fax_inbound_20170313_133831_recv0943.pdf</destination>
</delivered_to>
<delivered_to>
<type>Print</type>
<destination>LOCAL DESTINATION</destination>
</delivered_to>
<location />
<caller_name>NAME OF THE CALLER</caller_name>
<caller_number>5551239999</caller_number>
</inbound_fax_entry>
<inbound_fax_entry>
...
</inbound_fax_entry>
</response>
The code mentioned above produces the following output in the debugTextBox1 control:
Key=response,value=
Success
fax_inbound_20170313_133831_recv0943.pdf
complete
2017-03-15T13:31:06
5551239999
12
3555
Customer Service
...
I can see that the it's just identifying the first key response and then stripping all of the other keys and displaying the values. Honestly, I don't need the key names, my ultimate goal is to do some lite analysis (count how many faxes we've received, identify errors, etc) and then convert the data to CSV that can be exported. If I could just get it to enumerate everything between each inbound_fax_entry keypair I could easily chop up the data like I want.
Any suggestions would be appreciated.
Edit I was thinking about it and I do need the key names as well their corresponding values; not every fax has every XML element populated so I need to look for the inbound_fax_entry to delineate a new line in my csv and then record the values for each element, even if there's no data.
I believe your debugTextBox1 line needs to read el.Key, rather than el.Name.
Otherwise, you could try:
foreach (var el in xdoc.Elements())
{
debugTextBox1.Text = string.Format("Key={0},value={1}", el.Attributes["key"].Value.ToString(), el.Attributes["value"].Value.ToString());
}
This question is similar to How to read key/value in xml file and the answers there may be helpful.

Most efficient way to determine how class use to Deserialize XML

I have many .xsd files for many xml schemas
example
XML 1.0 - xml_1_0.xml
<?xml version="1.0" encoding="UTF-8"?>
<cars version="1.00">
<car>Honda</car>
<car>Ferrari</car>
</cars>
XML 2.0 - xml_2_0.xml
<?xml version="1.0" encoding="UTF-8"?>
<cars version="2.00">
<car>
<name>Honda</name>
<color>White</color>
</car>
<car>
<name>Honda</name>
<color>Red</color>
</car>
</cars>
I create my classes from .xsd like this
xsd.exe cars_1_0.xsd /c
xsd.exe cars_2_0.xsd /c
And Deserialize like this:
foreach(string file in files) {
XmlDocument doc = new XmlDocument();
doc.Load(file);
string version = doc.SelectSingleNode("/Cars/#version").Value;
if(version == "1.00")
{
Stream reader = new FileStream(file, FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(v1.Cars));
v1.Cars XML = new v1.Cars();
XML = (v1.Cars)serializer.Deserialize(reader);
}
else if(version == "2.00")
{
Stream reader = new FileStream(file, FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(v2.Cars));
v2.Cars XML = new v2.Cars();
XML = (v2.Cars)serializer.Deserialize(reader);
}
}
Does anyone know a better way to do this, or have a better performance?
You have several options, depending on how far you want to take this. One fairly non invasive option would be to not use XmlDocument and avoid loading the stream more than once. For example, your existing code could be simplified/streamlined to :
foreach (string file in files)
{
using (var stream = new FileStream(file, FileMode.Open))
{
var settings = new XmlReaderSettings();
settings.CloseInput = false;
string version = "";
using (var xmlReader = XmlReader.Create(stream))
{
if (xmlReader.ReadToFollowing("Cars"))
{
version = xmlReader.GetAttribute("version");
}
else
{
throw new XmlException("Could not get 'version' attribute of 'Cars' root element!");
}
}
stream.Position = 0;
if (version == "1.00")
{
XmlSerializer serializer = new XmlSerializer(typeof(v1.Cars));
v1.Cars XML = new v1.Cars();
XML = (v1.Cars)serializer.Deserialize(stream);
}
else if (version == "2.00")
{
XmlSerializer serializer = new XmlSerializer(typeof(v2.Cars));
v2.Cars XML = new v2.Cars();
XML = (v2.Cars)serializer.Deserialize(stream);
}
}
}
Since you're just reading off the root element, you might even be able to get away with deserializing from the XmlReader and not have to reset the position on the FileStream.
This avoids the overhead of loading the entire file twice (once for XmlDocument, then again for XmlSerializer) - and particularly avoids the memory overhead of creating a DOM for each document.
A more nuclear option would be implementing IXmlSerializable on a set of custom classes, which would have custom logic in the ReadXml methods to parse the version attribute and instantiate the correct child type(s) - e.g. a CarCollection class that has a List<Car> property, where Car is an abstract class that has CarV1 and CarV2 as descendants. This would be about as efficient as you could get (and offer very fine grained control over your class hierarchy design), but would eliminate the possibility of using xsd.exe to generate your classes.

XmlReader : data invalid exception

I'm trying to read an XML file and I get an XmlException : "data at the root level is invalid. Line 1, position 1".
Here is the content of the XML file :
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<root>
<Materials override="TRUE">
<Material name="" diffuse="" />
</Materials>
</root>
And here is my code :
using (FileStream fstr = File.OpenRead(sFullPath))
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Document;
fstr.Position = 0;
using (XmlReader xmlReader = XmlReader.Create(fstr, settings))
{
while (xmlReader.Read())
{
}
}
}
The exception is raised by the call to Read().
I've been searching for an answer on different sites, had a look at the MSDN too, but can't solve my problem.
My code is taken from http://www.codeproject.com/Articles/318876/Using-the-XmlReader-class-with-Csharp but I tried different snippets too.
I also checked the encoding of my file on Notepad++, tried both UTF-8 and UTF-8 without BOM, didn't make a change.
I'm stuck on this for a couple of days and I'm running out of ideas.
Thanx for your help!
Edit : removed the "..." in the snippet to avoid confusing people. I also did a try with :
using (XmlTextReader xmlReader = new XmlTextReader(fstr))
and it appears that xmlReader.Encoding is returning null, whereas my file is encoded to UTF-8.

C# check if XML file is not corrupted before deserialization

I didn't find anything for my problem in internet.
I deserialize data for playlists.
He is my code :
using (var fs = new FileStream("playlist.xml", FileMode.OpenOrCreate))
{
XmlSerializer xml = new XmlSerializer(typeof(ObservableCollection<Playlist>));
if (fs.Length > 0)
pl = (ObservableCollection<Playlist>)xml.Deserialize(fs);
else
pl = new ObservableCollection<Playlist>();
}
Here is the result XML :
<?xml version="1.0"?>
<ArrayOfPlaylist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Playlist>
<Name>Playlist1</Name>
<List>
<Media>
<path>C:\Users\Tchiko\Videos\Suit Tie (Official Lyric Video).mp4</path>
<name>Suit Tie (Official Lyric Video).mp4</name>
<type>Video</type>
</Media>
</List>
</Playlist>
<Playlist>
<Name>Hip hop</Name>
<List>
<Media>
<path>C:\Users\Tchiko\Videos\Suit Tie (Official Lyric Video).mp4</path>
<name>Suit Tie (Official Lyric Video).mp4</name>
<type>Video</type>
</Media>
</List>
</Playlist>
</ArrayOfPlaylist>
Before loading my playlist, I want to check if a user corrupted the file by hand.
I need to check if the format XML is well, in order to avoid conflicts after deserialization.
EDIT :
Version to avoid error for not-well format :
using (var fs = new FileStream("playlist.xml", FileMode.OpenOrCreate))
{
try
{
XmlSerializer xml = new XmlSerializer(typeof(ObservableCollection<Playlist>));
if (fs.Length > 0)
pl = (ObservableCollection<Playlist>)xml.Deserialize(fs);
else
pl = new ObservableCollection<Playlist>();
}
catch (Exception ex)
{
pl = new ObservableCollection<Playlist>();
}
}
Thanks for helps
To ensure XML validity you'll need to define an XML Schema. An XML Schema declares what tags, in what order and with what type of values are allowed in your XML.
Here is an article about how to validate XML against a Schema.
If your XML is not well-formed (as in, the user didn't close a tag or something of the sort), deserialization will fail and you'll get an InvalidOperationException with more details in the InnerException. See XmlSerializer.Deserialize() on MSDN.

List XML Serialization Doubling Elements

I'm trying to serialize an object as XML and have been using a little tester to experiment with different object behaviors when serializing as XML. I know binary serializers are deep and that XML is shallow. However, it does seem that it tries to serialize a List composed within another object when using XML.
My issue is that I get copied data when I serialize a List. Code and output follow:
class Program
{
static void Main(string[] args)
{
TestSerializer original = new TestSerializer();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(original.GetType());
x.Serialize(Console.Out, original);
Console.WriteLine("\n\n\n");
using (MemoryStream stream = new MemoryStream())
{
x.Serialize(stream, original);
stream.Seek(0, SeekOrigin.Begin);
TestSerializer copy = x.Deserialize(stream) as TestSerializer;
x.Serialize(Console.Out, copy);
}
Console.ReadLine();
}
}
public class TestSerializer
{
public List<string> words = new List<string>();
public TestSerializer()
{
words.Add("word");
words.Add("anotherword");
}
}
And the corresponding output:
<?xml version="1.0" encoding="IBM437"?>
<TestSerializer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<words>
<string>word</string>
<string>anotherword</string>
</words>
</TestSerializer>
<?xml version="1.0" encoding="IBM437"?>
<TestSerializer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<words>
<string>word</string>
<string>anotherword</string>
<string>word</string>
<string>anotherword</string>
</words>
</TestSerializer>
As you can see, the list content is doubled up when "original" is serialized, then deserialized to "copy". Is there something I am missing as far this is concerned? It seems like there should not be duplicated data.
Put a breakpoint on the constructor of TestSerializer class. You will notice that it is called e.g. on the following line:
TestSerializer copy = x.Deserialize(stream) as TestSerializer;
So when you deserialize the object following happens
First instance of TestSerializer is created (populates the two values in the list) and it executes the default constructor
Dezerialization adds the items from the stream to the created object (now you have 4 items)

Categories