简体   繁体   中英

Inserting new XML Data at specific Point in document C# Using LINQ

Hello everyone I have been searching all day looking for a way to add data into a existing XML document using LINQ. All that I can seem to find is how to create new elements and how to add them to just the end of the file if you will. I am trying to create an application that will allow me to add FTP accounts and update existing ones from Filezilla Ftp client without manually having to do so. Here is the XML document.

    <FileZillaServer>
  <Settings>
    <Item name="Admin port" type="numeric">14147</Item>
  </Settings>
  <Groups />
  <Users>
    <User Name="ServerManager">
      <Option Name="Pass">6</Option>
      <Option Name="Group">
      </Option>
      <Option Name="Bypass server userlimit">0</Option>
      <Option Name="User Limit">0</Option>
      <Option Name="IP Limit">0</Option>
      <Option Name="Enabled">1</Option>
      <Option Name="Comments">
      </Option>
      <Option Name="ForceSsl">0</Option>
      <IpFilter>
        <Disallowed />
        <Allowed />
      </IpFilter>
      <Permissions>
        <Permission Dir="C:\Dayz Server Manager">
          <Option Name="FileRead">1</Option>
          <Option Name="FileWrite">1</Option>
          <Option Name="FileDelete">1</Option>
          <Option Name="FileAppend">1</Option>
          <Option Name="DirCreate">1</Option>
          <Option Name="DirDelete">1</Option>
          <Option Name="DirList">1</Option>
          <Option Name="DirSubdirs">1</Option>
          <Option Name="IsHome">1</Option>
          <Option Name="AutoCreate">0</Option>
        </Permission>
        <Permission />
      </Permissions>
      <SpeedLimits DlType="0" DlLimit="10" ServerDlLimitBypass="0" UlType="0" UlLimit="10" ServerUlLimitBypass="0">
        <Download />
        <Upload />
      </SpeedLimits>
    </User>
  </Users>
</FileZillaServer>

I have so far been able to figure out how to get into the correct place of the document using a LINQ query but not really sure how to add to it. Any help would be greatly appreciated. Below is the Query I'm using to get me in the right place.

                var InsHere = from name in doc.Descendants("Users")
                           where name.Element("User").Attribute("Name").Value == "ServerManager"
                           select name.Element("User").Element("Permissions")
                           .Element("Permission").Attribute("Dir").Value;

So in this case what I'm needing to do is would be add another directory to the for a specific user that will change on a consistent basis. Many situations will require that I create a whole new user. Thank you in advance for any help.

Okay so after looking at the examples and the answers given I think I have begin to get closer. However I am still getting either a "Parent is Missing" Exception or "Object reference" Error. Here is the code below that I am using to try and insert.

            try
        {

            XDocument doc = XDocument.Load("C:/users/vildez/desktop/test.xml");

            XElement test = new XElement("TEST", "this is data");

            var InsertPoint = from user in doc.Descendants("Users")
                              where user.Element("User").Attribute("Name").Value == "ServerManager"
                              select user.Element("User").Element("Permissions").Element("Permission").Name;

            foreach (var v in InsertPoint)
            {
                XElement Perm = doc.Element(v.ToString());
                Perm.AddAfterSelf(test);
            }

            doc.Save("C:/users/vildez/desktop/123.xml");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            Application.Exit();
        }

If you are trying to add a new permission element you can use AddAfterSelf (see: http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(System.Xml.Linq.XNode.AddAfterSelf);k(AddAfterSelf);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5);k(DevLang-csharp)&rd=true ). Your Linq would have to change to remove the .Attribute("Dir").Value

If you are trying to and a second "Dir" attribute, you can't do that. XML attributes should be unique.

--Response to comment--

"... I am not trying to create another Attribute but another Element that will basically clone the one's before it and just different directory paths etc"

Sounds like you want to add an additional Permission element with a modified directory attribute.

var InsHere = from name in doc.Descendants("Users")
              where name.Element("User").Attribute("Name").Value == "ServerManager"
              select name.Element("User").Element("Permissions").Element("Permission");
var newElement = new XElement(InsHere); // this clones the found Permission element
newElement.Attribute("Dir").Value = "the new directory path to add";
InsHere.AddAfterSelf(newElement);

See if that accomplishes what you desire.

Just think about it systematically. What does it take to add an xml element to a specific xml element in a document?

  1. Find the xml element you want to add to.
  2. Get/Create the xml element you want to add.
  3. Add the xml element.

So what does that mean in this document?

  1. Find the user you wish to add to. If found:
  2. Create the new permission.
  3. Add the new permission to the Permissions element for the user.
public XElement AddDirectoryPermissionForUser(XDocument doc, string userName, string dir)
{
    // I prefer using xpath queries over full linq queries
    var xpath = String.Format("//User[@Name='{0}']", userName);
    var user = doc.XPathSelectElement(xpath); // 1
    if (user != null)
    {
        var permission = CreatePermissionForDir(dir); // 2
        user.Element("Permissions").Add(permission); // 3
        return permission;
    }
    return null;
}

I'm not sure what it is you're doing exactly but this should give you a nice starting point on how to proceed.

You can add an XElement onto, before, after any other XElement.

A demonstration of adding/adjusting an option based on your query:

string userName = "ServerManager";
string directory = "some directory";
string optionName = "FileRead";
int optionSetting = 1;

XElement doc = XElement.Load("C:/users/vildez/desktop/test.xml");

XElement user = doc.Descendants("User")
                   .FirstOrDefault(user => 
                      user.Attribute("Name").Value == userName);

// Add user if it doesn't exist
if (user == null)
{
    XElement users = doc.Element("Users");
    if (users == null)
        doc.Add(users = new XElement("Users"));
    users.Add(user = new XElement("User",
        new XAttribute("Name", userName)));
}

XElement permission = user.Descendants("Permission")
                          .FirstOrDefault(perm => 
                          perm.Attribute("Dir").Value == directory);

// Add permission if it doesn't exist
if (permission == null)
{
    XElement permissions = user.Element("Permissions");
    if (permissions == null)
        user.Add(permissions = new XElement("Permissions"));
    permissions.Add(permission = new XElement("Permission",
        new XAttribute("Dir", directory)));
}
XElement option = permission.Elements("Option")
        .FirstOrDefault(op => op.Attribute("Name").Value == optionName);

// Add option if it doesn't exist
if (option == null)
    permission.Add(option = new XElement("Option", 
             new XAttribute("Name", optionName)));

option.Value = optionSetting.ToString();

I can't quite figure out what you are trying to accomplish, but the above will add a new Option to a Permission, and if the Permission doesn't exist, add it.

I only ever use XElement (not XDocument) for the root node, so I changed it to be that (an XElement). The above code may work as is with using an XDocument.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM