0

Each time i get a request from a user, i have to serialize and append it , to an existing xml file like this :

<LogRecords>
  <LogRecord>
    <Message>Some messagge</Message>
    <SendTime>2017-12-13T22:04:40.1109661+01:00</SendTime>
    <Sender>Sender</Sender>
    <Recipient>Name</Recipient>
  </LogRecord>
  <LogRecord>
    <Message>Some message too</Message>
    <SendTime>2017-12-13T22:05:08.5720173+01:00</SendTime>
    <Sender>sender</Sender>
    <Recipient>name</Recipient>
  </LogRecord>
</LogRecords>

Currently Serializing data in this way (which works fine):

var stringwriter = new StringWriter();
var serializer = new XmlSerializer(object.GetType());

serializer.Serialize(stringwriter, object);
var smsxmlStr = stringwriter.ToString();

var smsRecordDoc = new XmlDocument();
smsRecordDoc.LoadXml(smsxmlStr);

var smsElement = smsRecordDoc.DocumentElement;

var smsLogFile = new XmlDocument();
smsLogFile.Load("LogRecords.xml");

var serialize = smsLogFile.CreateElement("LogRecord");
serialize.InnerXml = smsElement.InnerXml;
smsLogFile.DocumentElement.AppendChild(serialize);

smsLogFile.Save("LogRecords.xml");

And the properties class

[XmlRoot("LogRecords")]
public class LogRecord
{
    public string Message { get; set; }
    public DateTime SendTime { get; set; }
    public string Sender { get; set; } 
    public string Recipient { get; set; }
}

But what i want to do is to load the file, navigate to the last element/node of it and append a new List<LogRecord> and save, so i can easily deserialize later. I have tried various ways using XPath Select Methods like SelectSingleNode and SelectNodes but since i am junior with c# i haven't manage to make them work properly. Does anyone have any idea on how to serialize and append properly? Thank you

esmehsnj
  • 162
  • 2
  • 17
  • See [Fastest way to add new node to end of an xml?](https://stackoverflow.com/q/849043/3744182) or [Appending an existing XML file with XmlWriter](https://stackoverflow.com/q/20922835/3744182) for some tricks that might work. – dbc Dec 14 '17 at 18:22
  • To deserialize **what**? If all the data, then [use a list](https://stackoverflow.com/a/47816774/5045688). If only the last element, then use list (sic!) and take the last element from it. Or use the `ReadToFollowing` in a loop, until you reach the last element. – Alexander Petrov Dec 14 '17 at 20:47

3 Answers3

1

Your approach (and most of the answers given to date) rely on having all of the log file in memory in order to append more records to it. As the log file grows, this could cause issues (such as OutOfMemoryException errors) down the road. Your best bet is to use an approach that streams the data from the original file into a new file. While there might be a few bugs in my untested code. The approach would look something like the following:

// What you are serializing
var obj = default(object);

using (var reader = XmlReader.Create("LogRecords.xml"))
using (var writer = XmlWriter.Create("LogRecords2.xml"))
{
  // Start the log file
  writer.WriteStartElement("LogRecords");
  while (reader.Read())
  {
    // When you see a record in the original file, copy it to the output
    if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "LogRecord")
    {
      writer.WriteNode(reader.ReadSubtree(), false);
    }
  }

  // Add your additional record(s) to the output
  var serializer = new XmlSerializer(obj.GetType());
  serializer.Serialize(writer, obj);

  // Close the tag
  writer.WriteEndElement();
}

// Replace the original file with the new file.
System.IO.File.Delete("LogRecords.xml");
System.IO.File.Move("LogRecords2.xml", "LogRecords.xml");

Another idea to consider, does the log file need to be a valid XML file (with the <LogRecords> tag at the start and finish? If you omit the root tag, you could simply append the new records at the bottom of the file (which should be very efficient). You can still read the XML in .Net by creating an XmlReader with the right ConformanceLevel. For example

var settings = new XmlReaderSettings() 
{ 
  ConformanceLevel  = ConformanceLevel.Fragment 
};
using (var reader = XmlReader.Create("LogRecords.xml", settings)) 
{ 
  // Do something with the records here
}
erdomke
  • 4,980
  • 1
  • 24
  • 30
0

Try using xml linq :

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{

    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XDocument doc = XDocument.Load(FILENAME);

            LogRecord record = doc.Descendants("LogRecord").Select(x => new LogRecord()
            {
                Message = (string)x.Element("Message"),
                SendTime = (DateTime)x.Element("SendTime"),
                Sender = (string)x.Element("Sender"),
                Recipient = (string)x.Element("Recipient")
            }).OrderByDescending(x => x.SendTime).FirstOrDefault();

        }
    }
    public class LogRecord
    {
        public string Message { get; set; }
        public DateTime SendTime { get; set; }
        public string Sender { get; set; }
        public string Recipient { get; set; }
    }


}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

You can perform it by using XDocument like this;

        XDocument doc = XDocument.Load("LogRecords.xml");
        //Append Node
        XElement logRecord = new XElement("LogRecord");
        XElement message = new XElement("Message");
        message.Value = "Message";
        XElement sendTime = new XElement("SendTime");
        sendTime.Value = "SendTime";
        XElement sender = new XElement("Sender");
        sender.Value = "Sender";
        XElement recipient = new XElement("Recipient");
        recipient.Value = "Recipient";
        logRecord.Add(message);
        logRecord.Add(sendTime);
        logRecord.Add(sender);
        logRecord.Add(recipient);
        doc.Element("LogRecords").Add(logRecord);
        //Append Node
        doc.Save("LogRecords.xml");
lucky
  • 12,734
  • 4
  • 24
  • 46