2

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:

  1. load the file into an XmlDocument.
  2. navigate to the node I want to append beneath (here the node name is Shortcuts).
  3. create some type of writer and then serialize the object.
  4. 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);

  }

}
cbutler
  • 833
  • 13
  • 36
  • Are you looking for something like `XmlNodeExtensions.SerializeToXmlElement()` from [How to create sets of the serialized objects C#](https://stackoverflow.com/a/29307126/3744182) ? – dbc Jul 05 '17 at 21:00
  • 1
    You might consider switching from `XmlDocument` to [LINQ to XML](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml) and `XDocument`. It's a newer API that is easier to use and likely to be better supported in places like .Net core. – dbc Jul 05 '17 at 21:03
  • Thanks dbc. I saw a few solutions using LINK to XML and was wondering if that would be better. I might try that if I can get serialization working soon – cbutler Jul 05 '17 at 21:18
  • You're welcome. Did [How to create sets of the serialized objects C#](https://stackoverflow.com/a/29307126/3744182) answer your question? – dbc Jul 05 '17 at 21:27
  • This post actually gave me what I was looking for: [How to serialize multiple objects into an existing XmlDocument, without having the namespaces on each component?](https://stackoverflow.com/a/14772240/6596291) – cbutler Jul 05 '17 at 21:29

2 Answers2

3

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);
}
cbutler
  • 833
  • 13
  • 36
0
  1. load the file into an XmlDocument.
  2. navigate to the node I want to append beneath (here the node name is Shortcuts).
  3. create some type of writer and then serialize the object.
  4. 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

user270576
  • 987
  • 10
  • 16
  • I think this is close but right now I am getting an exception when I run the code. my xml looks like this: . the new node should go under shortcuts – cbutler Jul 05 '17 at 21:14