1

I am trying to add a single line/node (provided below) into an XML:

<Import Project=".www\temp.proj" Condition="Exists('.www\temp.proj')" />

The line could be under the main/root node of the XML:

<Project Sdk="Microsoft.NET.Sdk">

The approach I used:

        XmlDocument Proj = new XmlDocument();
        Proj.LoadXml(file);
        XmlElement root = Proj.DocumentElement;
        // Not sure about the next steps
        root.SetAttribute("not sure", "not sure", "not sure");

Though I don't exactly know how to add that line in the XML, cause it was my first try on directly editing XML files, the error caused an extra problem over it.

I get this error on my first attempt:

C# "loadxml" 'Data at the root level is invalid. Line 1, position 1.'

Know this error was a famous one, which some provided a variety of approaches in this link:

xml.LoadData - Data at the root level is invalid. Line 1, position 1

Unfortunately, most of the solutions are outdated, the answer didn't work on this case, and I don't know how to apply others on this case.

Provided/accepted answer on the link for that issue:

string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (xml.StartsWith(_byteOrderMarkUtf8))
{
    xml = xml.Remove(0, _byteOrderMarkUtf8.Length);
}

Basically it didn't work, cause xml.StartsWith seems not existing anymore, at the same time xml.Remove also doesn't exist.


Can you please provide a piece of code that bypass the error and add the line to the XML?

Edit: The sample XML file is provided in the comments section.

Kasrak
  • 1,509
  • 4
  • 24
  • 48

2 Answers2

2

For the Xml posted in the comment, I have used two approachs :

1 - XmlDocument

XmlDocument Proj = new XmlDocument();
Proj.Load(file);
XmlElement root = Proj.DocumentElement;
//Create node
XmlNode node = Proj.CreateNode(XmlNodeType.Element, "Import", null);

//create attributes
XmlAttribute attrP = Proj.CreateAttribute("Project");
attrP.Value = ".www\\temp.proj";

XmlAttribute attrC = Proj.CreateAttribute("Condition");
attrC.Value = "Exists('.www\\temp.proj')";

node.Attributes.Append(attrP);
node.Attributes.Append(attrC);

//Get node PropertyGroup, the new node will be inserted before it
XmlNode pG = Proj.SelectSingleNode("/Project/PropertyGroup");
root.InsertBefore(node, pG);

Console.WriteLine(root.OuterXml);

2 - Linq To Xml, by using XDocument

XDocument xDocument = XDocument.Load(file);

xDocument.Root.AddFirst(new XElement("Import", 
    new XAttribute[] 
    { 
        new XAttribute("Project", ".www\\temp.proj"), 
        new XAttribute("Condition", "Exists('.www\\temp.proj')") 
    }));

Console.WriteLine(xDocument);

Namespace to add for XDocument:

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

Both solutions give the same result, but the last one is simple.

I hope you find this helpful.

Mohammed Sajid
  • 4,778
  • 2
  • 15
  • 20
  • Thanks, sorry for being late a bit, please give a short time to have a look at your nice answer. +1 – Kasrak Jun 26 '20 at 22:22
  • Liked both approaches and they show the steps needed clearly. have 2 related questions if you be able to answer, first is how it bypassed that error? the XML is the same, and seems both used same methods at first (Load), how it bypassed that crazy error about the first char? – Kasrak Jun 26 '20 at 22:34
  • Second, if needed how can I move the inserted line before closing the root/wrapper element ()? Right before the last line? thanks again. – Kasrak Jun 26 '20 at 22:36
  • Same as you like more and prefer the second approach. – Kasrak Jun 26 '20 at 22:38
  • @Sajid , Can you please answer this part of my question, what was the reason for that error? How we avoided it? – Kasrak Jun 27 '20 at 07:56
  • 1
    @Kasrak sorry for late answer, i will check and answer you – Mohammed Sajid Jun 27 '20 at 08:23
  • 1
    1 question : *how it bypassed that error?* you have used `.LoadXml(filePath)` instead `.Load(filePath)` because `.LoadXml(filePath)` need xml string like a parameter not file path. 2 question : for `XmlDocument`, use `.InsertAfter()` instead `.InsertBefore()` like``root.InsertAfter(node, pG);`` for `XDocument`, use `.Add()` instead `.AddFirst()` like``xDocument.Root.Add(new ...)``. i hope this answer to your questions. – Mohammed Sajid Jun 27 '20 at 09:16
0

Would it be possible for you to use the official MSBuild libraries?(https://www.nuget.org/packages/Microsoft.Build/)
I'm not sure which nuget package is actually required to read and edit project files only.

I've tried to programatically edit MSBuild project files directly and can not recommend it. It broke regulary due to unexpected changes... The MSBuild library does a good job in editing project files and e.g. adding Properties, Items or Imports.

halliba
  • 309
  • 2
  • 8