C#: how to serialize/deserialize object of EventBookmark class - c#

I use EventLogWatcher to track events from logs. I need to save/restore bookmark events after my program stopped/started. Bookmark of events is represented by EventBookmark class https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventbookmark?view=netframework-4.8 and it is inherited from ISerializable interface. BUT I cannot serialize object of that class. Because it does not have default ctor, ctor with no parameters.
I am not familiar with C# very good because I am using it during a week. Can you help me please to understand why this class inherited from ISerializable interface while I cannot serialize an object of that class? It looks strange for me like I do not understand something but I would like to know that.
Moreover contructor
EventBookmark(SerializationInfo, StreamingContext) and GetObjectData(SerializationInfo, StreamingContext) are protected.
So, how to serialize and deserialize object of EventBookmark class?
UPDATED (working code), thank you jdweng for help
namespace ConsoleApp
{
using System;
public class Program
{
static void Main(string[] args)
{
string logName = "Application";
string filter = "*";
System.Diagnostics.Eventing.Reader.EventLogQuery eventQuery = new System.Diagnostics.Eventing.Reader.EventLogQuery(
logName,
System.Diagnostics.Eventing.Reader.PathType.LogName,
filter
);
System.Diagnostics.Eventing.Reader.EventLogReader eventLogReader = new System.Diagnostics.Eventing.Reader.EventLogReader(eventQuery);
System.Diagnostics.Eventing.Reader.EventRecord eventRecord = eventLogReader.ReadEvent();
System.Diagnostics.Eventing.Reader.EventBookmark eventBookmark = eventRecord.Bookmark;
if (eventBookmark != null)
{
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.Stream stream = new System.IO.MemoryStream();
formatter.Serialize(stream, eventBookmark);
stream.Position = 0;
System.IO.StreamReader reader = new System.IO.StreamReader(stream);
string result = reader.ReadToEnd();
Console.WriteLine("event bookmark: {0}", result);
stream.Position = 0;
System.Diagnostics.Eventing.Reader.EventBookmark newEventBookmark = (System.Diagnostics.Eventing.Reader.EventBookmark)formatter.Deserialize(stream);
if (newEventBookmark != null/* && eventBookmark.Equals(newEventBookmark)*/)
{
Console.WriteLine("Deserialization successfully passed");
}
else
{
Console.WriteLine("Deserialization failed");
}
}
else
{
Console.WriteLine("cannot read event bookmark");
}
Console.ReadKey();
}
}
}

Related

C#: Should Streams be closed/disposed in called method?

I have a method that I am writing in C# which accepts a string which contains an XML document, and an array of streams that are XSDs. The string document is validated against the XSDs:
private static XmlValidationResult ValidateDocumentInternal(string document, params Stream[] xsdStreams)
{
XmlReaderSettings settings = new XmlReaderSettings
{
ValidationType = ValidationType.Schema
};
foreach (var xsdStream in xsdStreams)
{
using (xsdStream)
{
XmlReader xmlReader = XmlReader.Create(xsdStream);
try
{
settings.Schemas.Add(null, xmlReader);
}
finally
{
xmlReader.Close();
}
}
}
var validationErrors = new List<string>();
settings.ValidationEventHandler += (object sender, System.Xml.Schema.ValidationEventArgs e) =>
{
validationErrors.Add($"({e.Exception.LineNumber}): {e.Message}");
};
using (var stream = document.ToStream())
{
var reader = XmlReader.Create(stream, settings);
while (reader.Read())
{
}
}
return new XmlValidationResult
{
Success = validationErrors.Count == 0,
ValidationErrors = validationErrors
};
}
My question is, should this method be disposing of the XSD streams or should that be the responsibility of the caller? Imagine the following code which passes in the document and XSD and expects ValidateDocumentInternal to dispose the XSD stream:
var document = GetDocument();
Stream xsd = GetXSD();
var validationResult = ValidateDocumentInternal(document, xsd);
or should it be like (not disposing of the stream in ValidateDocumentInternal):
var document = GetDocument();
using (Stream xsd = GetXSD()) {
var validationResult = = ValidateDocumentInternal(document, xsd);
}
or alternatively should I just pass in a bool saying whether to dispose or not?
I think it is the caller's responsibility - it is a parameter given from the function by someone else. The function can't know if it is used in another context and and change that it will do to is is actually a "side effect"... which I personally strongly try to avoid

How do I write raw data from any data type to a file with C#?

I'm writing a Windows app in C#. I have a custom data type that I need to write as raw data to a binary file (not text/string based), and then open that file later back into that custom data type.
For example:
Matrix<float> dbDescs = ConcatDescriptors(dbDescsList);
I need to save dbDescs to file blah.xyz and then restore it as Matrix<float> later. Anyone have any examples? Thanks!
As I've mentioned, the options are overwhelming and this question comes with a ton of opinions as far as which one is the best. With that being said, BinaryFormatter could prove to be useful here as it serializes and deserializes object (along with graphs of connected objects) in binary.
Here's the MSDN link that explains the usage: https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter(v=vs.110).aspx
Just in case that link fails down the line and because I'm too lazy to provide my own example, here's an example from MSDN:
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
public class App
{
[STAThread]
static void Main()
{
Serialize();
Deserialize();
}
static void Serialize()
{
// Create a hashtable of values that will eventually be serialized.
Hashtable addresses = new Hashtable();
addresses.Add("Jeff", "123 Main Street, Redmond, WA 98052");
addresses.Add("Fred", "987 Pine Road, Phila., PA 19116");
addresses.Add("Mary", "PO Box 112233, Palo Alto, CA 94301");
// To serialize the hashtable and its key/value pairs,
// you must first open a stream for writing.
// In this case, use a file stream.
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, addresses);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
static void Deserialize()
{
// Declare the hashtable reference.
Hashtable addresses = null;
// Open the file containing the data that you want to deserialize.
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Deserialize the hashtable from the file and
// assign the reference to the local variable.
addresses = (Hashtable) formatter.Deserialize(fs);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
// To prove that the table deserialized correctly,
// display the key/value pairs.
foreach (DictionaryEntry de in addresses)
{
Console.WriteLine("{0} lives at {1}.", de.Key, de.Value);
}
}
}
Consider the Json.Net package (you can download it to your project via Nuget; the better way, or get it directly from their website).
JSON is just a string (text) that holds values for complex objects. It allows you to turn many (not all) objects into savable files easily which then can be pulled back. To serialize into JSON with JSON.net:
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Sizes = new string[] { "Small" };
string json = JsonConvert.SerializeObject(product);
And then to deserialize:
var product = JsonConvert.DeserializeObject(json);
To write the json to a file:
using (StreamWriter writer = new StreamWriter(#"C:/file.txt"))
{
writer.WriteLine(json);
}
I am not a Web Developer so I am not sure that JSON is Binary. Isnt it still text based? So here is what I know is a Binary Answer. Hope this Helps!
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BinarySerializerSample
{
class Program
{
public static void WriteValues(string fName, double[] vals)
{
using (BinaryWriter writer = new BinaryWriter(File.Open(fName, FileMode.Create)))
{
int len = vals.Length;
for (int i = 0; i < len; i++)
writer.Write(vals[i]);
}
}
public static double[] ReadValues(string fName, int len)
{
double [] vals = new double[len];
using (BinaryReader reader = new BinaryReader(File.Open(fName, FileMode.Open)))
{
for (int i = 0; i < len; i++)
vals[i] = reader.ReadDouble();
}
return vals;
}
static void Main(string[] args)
{
const double MAX_TO_VARY = 100.0;
const int NUM_ITEMS = 100;
const string FILE_NAME = "dblToTestx.bin";
double[] dblToWrite = new double[NUM_ITEMS];
Random r = new Random();
for (int i = 0; i < NUM_ITEMS; i++)
dblToWrite[i] = r.NextDouble() * MAX_TO_VARY;
WriteValues(FILE_NAME, dblToWrite);
double[] dblToRead ;
dblToRead = ReadValues(FILE_NAME, NUM_ITEMS);
int j = 0;
bool areEqual = true;
while (areEqual && j < NUM_ITEMS)
{
areEqual = dblToRead[j] == dblToWrite[j];
++j;
}
if (areEqual)
Console.WriteLine("Test Passed: Press any Key to Exit");
else
Console.WriteLine("Test Failed: Press any Key to Exit");
Console.Read();
}
}
}

Loading & Deserialising Many JSON files into Specific C# Classes

I have a problem with C# and Unity3D. I would like to read in a series of some to many JSON files from a single directory, and deserialise the data into one specific class for each file. I'm wondering if there's a fast way of looping that process so I don't need a large if/switch block or anything. Deserialisation is fine, but I'm having trouble actually assigning the data from each file to a list containing objects of the correct Type.
Note: The class name is the same as the filename. For example, if the filename is Cars.json, I want to find a Component called CarManager and use it to store the deserialised data in a List<Car> at CarManager.cars.
I'm inexperienced, and I don't really know how to work with Type references yet. If someone could explain how can I write the ProcessFile() method to successfully differentiate between Object types, so I can store the data for each file in Unity, I'd really appreciate it.
Cheers.
namespace Flight {
public class DatasetManager : MonoBehaviour {
private JsonSerializer serialiser;
private StreamReader streamReader;
private string path;
private string fileName;
private string extension;
public void Start() {
// Define Path
path = Application.dataPath + "/Data/";
extension = "json";
// Construct Serialiser
serialiser = new JsonSerializer();
serialiser.Converters.Add(new StringEnumConverter());
serialiser.NullValueHandling = NullValueHandling.Ignore;
// Import Data
Import();
}
private void Import() {
string[] files = Directory.GetFiles(path, "*." + extension);
if(files.Length == 0) return;
for(int i = 0; i < files.Length; i++) ProcessFile(files[i]);
}
private void ProcessFile(string xFile) {
streamReader = File.OpenText(xFile);
// Read Filename
string plural = Path.GetFileNameWithoutExtension(xFile);
string entity = plural.EndsWith("ies") ? plural.Substring(0,plural.Length-3) + "y" : plural.Substring(0,plural.Length-1);
string manager = entity + "Manager";
// Determine Entity & Manager Types
System.Type entityType = System.Type.GetType("Flight." + entity);
System.Type managerType = System.Type.GetType("Flight." + manager);
if(entityType == null || managerType == null) return;
// Determine List Type
System.Type listType = null;
listType = typeof(List<>).MakeGenericType(entityType);
if(listType == null) return;
// Acquire Data
List<dynamic> data = System.Activator.CreateInstance(listType) as List<dynamic>;
data = serialiser.Deserialize(streamReader, typeof(List<dynamic>)) as List<dynamic>;
if(data == null) return;
// Store Data in Game
GameObject theGame = GameObject.FindGameObjectWithTag("Game");
Component theComponent = theGame.GetComponent(manager);
FieldInfo field = managerType.GetField(plural.ToLower());
/*** How can I proceed from here? ***/
List<dynamic> theList = field.GetValue(theComponent) as List<dynamic>;
field.SetValue(theComponent, data);
}
}
}
The above code produces an ArgumentException:
System.Collections.Generic.List1[System.Object] cannot be converted to target type: System.Collections.Generic.List1[Flight.Car]
No worries, fixed it. The following modified code within ProcessFile() appears to work properly:
private void ProcessFile(string xFile) {
// ...
// ...
// Determine List Type
System.Type listType = typeof(List<>).MakeGenericType(entityType);
if(listType == null) return;
// Acquire Data
streamReader = File.OpenText(xFile);
object data = null;
data = System.Activator.CreateInstance(listType);
data = serialiser.Deserialize(streamReader, listType);
if(data == null) return;
// Store Data in Game
GameObject theGame = GameObject.FindGameObjectWithTag("Game");
Component theComponent = theGame.GetComponent(manager);
FieldInfo field = managerType.GetField(plural.ToLower());
field.SetValue(theComponent, data);
}

error in XML document. Unexpected XML declaration. XML declaration must be the first node in the document

There is an error in XML document (8, 20). Inner 1: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.
OK, I understand this error.
How I get it, however, is what perplexes me.
I create the document with Microsoft's Serialize tool. Then, I turn around and attempt to read it back, again, using Microsoft's Deserialize tool.
I am not in control of writing the XML file in the correct format - that I can see.
Here is the single routine I use to read and write.
private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }
public StoredMsgs Operation(string from, string message, FileAccess access) {
StoredMsgs list = null;
lock (objLock) {
ErrorMessage = null;
try {
if (!File.Exists(xmlPath)) {
var root = new XmlRootAttribute(rootName);
var serializer = new XmlSerializer(typeof(StoredMsgs), root);
if (String.IsNullOrEmpty(message)) {
from = "Code Window";
message = "Created File";
}
var item = new StoredMsg() {
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
using (var stream = File.Create(xmlPath)) {
list = new StoredMsgs();
list.Add(item);
serializer.Serialize(stream, list);
}
} else {
var root = new XmlRootAttribute("MessageHistory");
var serializer = new XmlSerializer(typeof(StoredMsgs), root);
var item = new StoredMsg() {
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
using (var stream = File.Open(xmlPath, FileMode.Open, FileAccess.ReadWrite)) {
list = (StoredMsgs)serializer.Deserialize(stream);
if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write)) {
list.Add(item);
serializer.Serialize(stream, list);
}
}
}
} catch (Exception error) {
var sb = new StringBuilder();
int index = 0;
sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
var err = error.InnerException;
while (err != null) {
index++;
sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
err = err.InnerException;
}
ErrorMessage = sb.ToString();
}
}
return list;
}
Is something wrong with my routine? If Microsoft write the file, it seems to me that it should be able to read it back.
It should be generic enough for anyone to use.
Here is my StoredMsg class:
[Serializable()]
[XmlType("StoredMessage")]
public class StoredMessage {
public StoredMessage() {
}
[XmlElement("From")]
public string From { get; set; }
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("Message")]
public string Message { get; set; }
}
[Serializable()]
[XmlRoot("MessageHistory")]
public class MessageHistory : List<StoredMessage> {
}
The file it generates doesn't look to me like it has any issues.
I saw the solution here:
Error: The XML declaration must be the first node in the document
But, in that case, it seems someone already had an XML document they wanted to read. They just had to fix it.
I have an XML document created my Microsoft, so it should be read back in by Microsoft.
The problem is that you are adding to the file. You deserialize, then re-serialize to the same stream without rewinding and resizing to zero. This gives you multiple root elements:
<?xml version="1.0"?>
<StoredMessage>
</StoredMessage
<?xml version="1.0"?>
<StoredMessage>
</StoredMessage
Multiple root elements, and multiple XML declarations, are invalid according to the XML standard, thus the .NET XML parser throws an exception in this situation by default.
For possible solutions, see XML Error: There are multiple root elements, which suggests you either:
Enclose your list of StoredMessage elements in some synthetic outer element, e.g. StoredMessageList.
This would require you to load the list of messages from the file, add the new message, and then truncate the file and re-serialize the entire list when adding a single item. Thus the performance may be worse than in your current approach, but the XML will be valid.
When deserializing a file containing concatenated root elements, create an XML writer using XmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment and iteratively walk through the concatenated root node(s) and deserialize each one individually as shown, e.g., here. Using ConformanceLevel.Fragment allows the reader to parse streams with multiple root elements (although multiple XML declarations will still cause an error to be thrown).
Later, when adding a new element to the end of the file using XmlSerializer, seek to the end of the file and serialize using an XML writer returned from XmlWriter.Create(TextWriter, XmlWriterSettings)
with XmlWriterSettings.OmitXmlDeclaration = true. This prevents output of multiple XML declarations as explained here.
For option #2, your Operation would look something like the following:
private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }
const string rootName = "MessageHistory";
static readonly XmlSerializer serializer = new XmlSerializer(typeof(StoredMessage), new XmlRootAttribute(rootName));
public MessageHistory Operation(string from, string message, FileAccess access)
{
var list = new MessageHistory();
lock (objLock)
{
ErrorMessage = null;
try
{
using (var file = File.Open(xmlPath, FileMode.OpenOrCreate))
{
list.AddRange(XmlSerializerHelper.ReadObjects<StoredMessage>(file, false, serializer));
if (list.Count == 0 && String.IsNullOrEmpty(message))
{
from = "Code Window";
message = "Created File";
}
var item = new StoredMessage()
{
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write))
{
file.Seek(0, SeekOrigin.End);
var writerSettings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true, // Optional; remove if compact XML is desired.
};
using (var textWriter = new StreamWriter(file))
{
if (list.Count > 0)
textWriter.WriteLine();
using (var xmlWriter = XmlWriter.Create(textWriter, writerSettings))
{
serializer.Serialize(xmlWriter, item);
}
}
}
list.Add(item);
}
}
catch (Exception error)
{
var sb = new StringBuilder();
int index = 0;
sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
var err = error.InnerException;
while (err != null)
{
index++;
sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
err = err.InnerException;
}
ErrorMessage = sb.ToString();
}
}
return list;
}
Using the following extension method adapted from Read nodes of a xml file in C#:
public partial class XmlSerializerHelper
{
public static List<T> ReadObjects<T>(Stream stream, bool closeInput = true, XmlSerializer serializer = null)
{
var list = new List<T>();
serializer = serializer ?? new XmlSerializer(typeof(T));
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
CloseInput = closeInput,
};
using (var xmlTextReader = XmlReader.Create(stream, settings))
{
while (xmlTextReader.Read())
{ // Skip whitespace
if (xmlTextReader.NodeType == XmlNodeType.Element)
{
using (var subReader = xmlTextReader.ReadSubtree())
{
var logEvent = (T)serializer.Deserialize(subReader);
list.Add(logEvent);
}
}
}
}
return list;
}
}
Note that if you are going to create an XmlSerializer using a custom XmlRootAttribute, you must cache the serializer to avoid a memory leak.
Sample fiddle.

Attempting to deserialize an empty stream?

I'm building a program for my school's swim lesson organization, and I'm saving the data using XML serialization, but I keep getting an error every time I try to deserialize the data, it says "Runtime Error: Attempting to Deserialize an Empty Stream."
Here is my code to deserialize the file and put it into a window.
public StudentProfile()
{
InitializeComponent();
using (var file = File.Open(FindStudent.studentName + ".xml", FileMode.OpenOrCreate))
{
var bformatter = new BinaryFormatter();
var mp = (Person)bformatter.Deserialize(file);
file.Close();
nameBox.Text += mp.studentName;
parentBox.Text += mp.parentName;
yearBox.Text += mp.year;
semesterBox.Text += mp.semester;
sessionBox.Text += mp.session;
ageGroupBox.Text += mp.ageGroup;
sessionTimeBox.Text += mp.sessionTime;
levelBox.Text += mp.level;
paymentTypeBox.Text += mp.paymentType;
amountBox.Text += mp.amount;
checkNumberBox.Text += mp.checkNumber;
datePaidBox.Text += mp.datePaid;
}
}
I've tried some solutions on here, BinaryFormatter: SerializationException, but it still doesn't work. Can you guys help me?
Edit: I solved my error, using a different method, here is the code I ended up using to deserialize it. If anyone wants the serialization code, then I'll give it
Stream file = File.Open(#"C:\Swimmers\" + FindStudent.studentName + ".xml", FileMode.Open);
BinaryFormatter bformatter = new BinaryFormatter();
Person mp = (Person)bformatter.Deserialize(file);
file.Close();
nameBox.Text += mp.studentName;
parentBox.Text += mp.parentName;
yearBox.Text += mp.year;
semesterBox.Text += mp.semester;
sessionBox.Text += mp.session;
ageGroupBox.Text += mp.ageGroup;
sessionTimeBox.Text += mp.sessionTime;
levelBox.Text += mp.level;
paymentTypeBox.Text += mp.paymentType;
amountBox.Text += mp.amount;
checkNumberBox.Text += mp.checkNumber;
datePaidBox.Text += mp.datePaid;
}
With a FileMode of OpenOrCreate, if the file hasn't existed yet, it creates the file with no content, and thus would fail deserialization. It would be better to use:
if (File.Exists(FindStudent.StudentName + ".xml"))
{
//Serialization logic
}
else
{
//default logic; create the file but don't deserialize
//expect the UI to be loaded blank
}
That is probably the error you are experiencing, because you are deserializing a newly created blank file.
I strongly recommend you System.Runtime.Serialization Namespace, from System.Runtime.Serialization.dll. It provides serializers implementation such as XML and JSON.
The following example uses the DataContractSerializer.
[DataContract]
public class Student
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
public void Save(string filePath)
{
using (var fs = File.Open(filePath, FileMode.Create))
{
DataContractSerializer serializer = new DataContractSerializer(typeof (Student));
serializer.WriteObject(fs, this);
}
}
public static Student Load(string filePath)
{
Student result = null; //or default result
try
{
using (var fs = File.OpenRead(filePath))
{
DataContractSerializer serializer = new DataContractSerializer(typeof (Student));
result = serializer.ReadObject(fs) as Student;
}
}
catch (Exception)
{
}
return result;
}
}
Usage example:
...
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "student1.xml");
var student = new Student
{
Name = "Student1",
Age = 10
};
student.Save(filePath);
var studentFromFile = Student.Load(filePath);
...
I hope it helps.

Categories

Resources