Member Avatar for lithium112

Hello,

I am trying to query an xml table. Here's the basic structure:

<Table>
    <GroupPermissions>
        <Group GroupName="Name">
            <ModuleRestricitons>
                <Module1>
                    <Button1 value="False" />
                </Module1>
            </ModuleRestrictions>
        </Group>
    </GroupPermissions>
</Table>

The logic I am tyring to perform is this: Where GroupName="Name" set button 1 value="True". There are many other groups, so I need a way to query that specific group and then set the button value within that group.

I have tried:

xmlDoc.Element("Table")
      .Element("GroupPermissions")
      .Element("Group")
      .Element("ModuleRestrictions")
      .Element("Module1")
      .Element("Button1")
      .Attribute("value")
      .SetValue("true");

But I don't see any way to set it to the specific group. At the moment, this sets the value for all groups. Does anyone know how to restrict this to a specific GroupName attribute? Thanks!

For this LINQ probably isn't the bext choice. Since you aren't really creating a collection just modifying one, a simple foreach loop would probably work better.

Also you would probably find it easier to work with System.Xml.Linq.XDocument:

void SetButton1Attribute(string fileName)
{
    XDocument xdoc = XDocument.Load(fileName);
    XElement rootElement = xdoc.Element("Table").Element("GroupPermissions");
    foreach (XElement xelem in rootElement.Elements())
    {
        if (xelem.Attribute("GroupName").Value == "Name")
        {
            xelem
                .Element("ModuleRestrictions")
                .Element("Module1")
                .Element("Button1").Attribute("value").Value = "False";
        }
    }
    xdoc.Save(fileName);
}    

If your actual xml structure is more complicated you can use the Elements collection and specify elements with a specific element name.

Also if you have the namespace set you will need to use XName instead of just a string. The Get method takes a string in the format - "{namespace uri}element name" and returns an XName object.

I haven't used Linq, but if you know how to use Linq with a List, then you should be able to use the following--it uses XmlSerializer. I see a discrepancy in what you posted above. There would be no need to do where GroupName="Name" if the file only contained one GroupName. So, I will assume an XML file that looks like the following:

Table.xml

<Table>
  <GroupPermissions>
    <Group GroupName="Group1">
      <ModuleRestrictions>
        <Module1>
          <Button1 value="true" />
        </Module1>
      </ModuleRestrictions>
    </Group>
    <Group GroupName="Group2">
      <ModuleRestrictions>
        <Module1>
          <Button1 value="false" />
        </Module1>
      </ModuleRestrictions>
    </Group>
  </GroupPermissions>
</Table>

To use XmlSerializer, we will use nested classes.

"Table", "GroupPermissions", "Group", "ModuleRestrictions", "Module1", and "Button1" are XML elements (these will be denoted by "XmlElement" or "XmlElementAttribute"). Each one of these will be it's own class.

"Group" contains an attribute named "GroupName". This will be denoted as an "XmlAttribute". Likewise, "Button1", contains an attribute named "value".

In the XML file "Table.xml", more than one "Group" element exists. So, we need to use something that allows for more than one instance of "Group"--we will use a List for this.

Let's get started.

Note: It is necessary to add using System.Xml.Serialization; statement to each class (file).

You can name the classes anything you want as long as you specify the actual name of the element in the XML file using "ElementName" (if "ElementName" isn't specified, XmlSerializer, will assume that the class name matches the element name). Likewise, with variable and class instance names.

I will start all of the class names with "Cls". I will then append the name of the XML element to it. "Table" in the XML file, will become a class called "ClsTable" in C#. Since, "GroupPermissions" is contained within "Table", I will call the class "ClsTableGroupPermissions". Likewise, "Group" is contained within "GroupPermissions", so I will call it "ClsTableGroupPermissionsGroup", etc...

Start by creating each of the classes (one per XML element. Each of the following will be it's own class: Table, GroupPermissions, Group, ModuleRestrictions, Module1, Button1) and adding a using System.Xml.Serialization; statement to each ".cs" file.

Note:

Each class name has the following above it: [XmlRootAttribute(ElementName = "<XML element name>")]

Each instance of a (child) class, has the following above it: [XmlElementAttribute(ElementName = "<XML element name>")]

Each attribute has the following above it: [XmlAttribute(AttributeName = "<XML attribute name>")]

ClsTable.cs

[XmlRootAttribute(ElementName = "Table")]
public class ClsTable
{
}//class

ClsTableGroupPermissions.cs

[XmlRootAttribute(ElementName = "GroupPermissions")]
public class ClsTableGroupPermissions
{
}//class

ClsTableGroupPermissionsGroup.cs

[XmlRootAttribute(ElementName = "Group")]
public class ClsTableGroupPermissionsGroup
{
}//class

ClsTableGroupPermissionsGroupModuleRestrictions.cs

[XmlRootAttribute(ElementName = "ModuleRestrictions")]
public class ClsTableGroupPermissionsGroupModuleRestrictions
{
}//class

ClsTableGroupPermissionsGroupModuleRestrictionsModule1.cs

[XmlRootAttribute(ElementName = "Module1")]
public class ClsTableGroupPermissionsGroupModuleRestrictionsModule1
{
}//class

ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1.cs

[XmlRootAttribute(ElementName = "Button1")]
public class ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1
{
}//class

Now, let's go back and add code to make use of our classes. "Table" is the root node, so "ClsTable" will be our "root" class. It will contain (an instance of) "GroupPermissions" (ClsTableGroupPermissions).

ClsTable.cs

[XmlRootAttribute(ElementName = "Table")]
public class ClsTable
{
    [XmlElementAttribute(ElementName = "GroupPermissions")]
    public ClsTableGroupPermissions GroupPermissions = new ClsTableGroupPermissions();

}//class

"GroupPermissions" node may contain one or more (instances of) "Group", so we will use a List to hold "Group" (ClsTableGroupPermissionsGroup)--List<ClsTableGroupPermissionsGroup>

ClsTableGroupPermissions.cs

[XmlRootAttribute(ElementName = "GroupPermissions")]
public class ClsTableGroupPermissions
{
    [XmlElementAttribute(ElementName = "Group")]
    public List<ClsTableGroupPermissionsGroup> Group = new List<ClsTableGroupPermissionsGroup>();
}//class

The "Group" node contains an attribute named "GroupName" and also contains (an instance of) "ModuleRestrictions" (ClsTableGroupPermissionsGroupModuleRestrictions). I also added a constructor to this class to make it easier to set the value of "GroupName".

Note: It's necessary to include the default constructor, or you'll receive the following error: InvalidOperation Exception was unhandled...There was an error reflecting type...

ClsTableGroupPermissionsGroup.cs

[XmlRootAttribute(ElementName = "Group")]
public class ClsTableGroupPermissionsGroup
{
    [XmlAttribute(AttributeName = "GroupName")]
    public string GroupName { get; set; }

    [XmlElementAttribute(ElementName = "ModuleRestrictions")]
    public ClsTableGroupPermissionsGroupModuleRestrictions ModuleRestrictions = new ClsTableGroupPermissionsGroupModuleRestrictions();

    //default constructor needed for XmlSerializer initialization
    public ClsTableGroupPermissionsGroup()
    {
    }//constructor

    public ClsTableGroupPermissionsGroup(string groupName)
    {
        this.GroupName = groupName;
    }//constructor
}//class

The "ModuleRestrictions" node contains (an instance of) "Module1" (ClsTableGroupPermissionsGroupModuleRestrictionsModule1).

ClsTableGroupPermissionsGroupModuleRestrictions.cs

[XmlRootAttribute(ElementName = "ModuleRestrictions")]
public class ClsTableGroupPermissionsGroupModuleRestrictions
{
    [XmlElementAttribute(ElementName = "Module1")]
    public ClsTableGroupPermissionsGroupModuleRestrictionsModule1 Module1 = new ClsTableGroupPermissionsGroupModuleRestrictionsModule1();
}//class

Hopefully you are seeing a pattern. "Module1" node contains (an instance of) "Button1" (ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1).

ClsTableGroupPermissionsGroupModuleRestrictionsModule1.cs

[XmlRootAttribute(ElementName = "Module1")]
public class ClsTableGroupPermissionsGroupModuleRestrictionsModule1
{
    [XmlElementAttribute(ElementName = "Button1")]
    public ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1 Button1 = new ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1();

}//class

The "Button1" node has an attribute named "value".

ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1.cs

[XmlRootAttribute(ElementName = "Button1")]
public class ClsTableGroupPermissionsGroupModuleRestrictionsModule1Button1
{
    [XmlAttribute(AttributeName = "value")]
    public bool value;
}//class
commented: Great! +15

The following is a continuation of my above post.

To serialize the XML (write the XML to file), we will create a static class called "ClsXmlHelper" and add an instance of our "Table" class (ClsTable).

ClsXmlHelper.cs:

public static class ClsXmlHelper
{
    //create instance of root attribute (Table) class
    public static ClsTable myClsTable = new ClsTable();

}//class

Add the following "using" statements:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

Serializing is quite simple:

public static class ClsXmlHelper
{

    //create instance of root attribute (Table) class
    public static ClsTable myClsTable = new ClsTable();

    public static void serializeToXml(string filename)
    {
        //writes Xml to file

        //create new instance of StreamWriter
        StreamWriter sw = new StreamWriter(filename);

        //create new instance of XmlSerializer(<your class>.GetType)
        XmlSerializer mySerializer = new XmlSerializer(myClsTable.GetType());

        //serialize - write data to file
        mySerializer.Serialize(sw, myClsTable);

        //close StreamWriter
        if (sw != null)
        {
            sw.Close();
        }//if

    }//serializeToXml
}//class

If you want to eliminate the namespaces and the XML declaration in the XML file, use the following instead:

public static class ClsXmlHelper
{

    //create instance of root attribute (Table) class
    public static ClsTable myClsTable = new ClsTable();

    public static void serializeToXml(string filename)
    {
        //writes Xml to file

        //create new instance of StreamWriter
        StreamWriter sw = new StreamWriter(filename);

        //create new instance of XmlSerializer(<your class>.GetType)
        XmlSerializer mySerializer = new XmlSerializer(myClsTable.GetType());

        //eliminate "xsd" and "xsi" namespaces
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add(string.Empty, "urn:none");

        //indent Xml and eliminate Xml declaration
        XmlWriterSettings myXmlWriterSettings = new XmlWriterSettings();
        myXmlWriterSettings.OmitXmlDeclaration = true;
        myXmlWriterSettings.Encoding = Encoding.UTF8;
        myXmlWriterSettings.Indent = true;

        //create instance of XmlWriter
        XmlWriter myXmlWriter = XmlWriter.Create(sw, myXmlWriterSettings);

        //serialize - write data to file
        //mySerializer.Serialize(sw, myClsTable);
        //mySerializer.Serialize(sw, myClsTable, ns);
        mySerializer.Serialize(myXmlWriter, myClsTable, ns);

        //close XmlWriter
        if (myXmlWriter != null)
        {
            myXmlWriter.Close();
        }//if

        //close StreamWriter
        if (sw != null)
        {
            sw.Close();
        }//if

    }//serializeToXml
}//class

And to deserialize (read) the XML:

public static class ClsXmlHelper
{

    //create instance of root attribute (Table) class
    public static ClsTable myClsTable = new ClsTable();

    public static void deserializeFromXml(string filename)
    {
        //reads Xml from file

        //create new instance of StreamReader
        StreamReader sr = new StreamReader(filename);

        //create new instance of XmlSerializer(<your class>.GetType)
        XmlSerializer mySerializer = new XmlSerializer(myClsTable.GetType());

        //deserialize - read data (uses instance of StreamReader)
        myClsTable = (ClsTable)mySerializer.Deserialize(sr);

        //close StreamReader
        if (sr != null)
        {
            sr.Close();
        }//if

    }//deserializeFromXml
}//class

Example of adding data:

private string filename = @"C:\temp\Table.xml";

//add Group - Group1
ClsTableGroupPermissionsGroup myGroup1 = new ClsTableGroupPermissionsGroup("Group1");
myGroup1.ModuleRestrictions.Module1.Button1.value = true;
ClsXmlHelper.myClsTable.GroupPermissions.Group.Add(myGroup1);

//add Group - Group2
ClsTableGroupPermissionsGroup myGroup2 = new ClsTableGroupPermissionsGroup("Group2");
myGroup2.ModuleRestrictions.Module1.Button1.value = false;
ClsXmlHelper.myClsTable.GroupPermissions.Group.Add(myGroup2);

//write data to XML file
ClsXmlHelper.serializeToXml(filename);

Example of showing first Group data:

private string filename = @"C:\temp\Table.xml";

//read data from XML file
ClsXmlHelper.deserializeFromXml(filename);

string msg = string.Empty;
msg = "GroupName: " + ClsXmlHelper.myClsTable.GroupPermissions.Group[0].GroupName;
msg += System.Environment.NewLine;
msg += "value: " + ClsXmlHelper.myClsTable.GroupPermissions.Group[0].ModuleRestrictions.Module1.Button1.value.ToString();

System.Windows.Forms.MessageBox.Show(msg);

Example of showing all Group data:

private string filename = @"C:\temp\Table.xml";

//read data from XML file
ClsXmlHelper.deserializeFromXml(filename);

string msg = string.Empty;
for (int i = 0; i < ClsXmlHelper.myClsTable.GroupPermissions.Group.Count; i++)
{
    msg += "GroupName: " + ClsXmlHelper.myClsTable.GroupPermissions.Group[i].GroupName;
    msg += System.Environment.NewLine;
    msg += "Value: " + ClsXmlHelper.myClsTable.GroupPermissions.Group[i].ModuleRestrictions.Module1.Button1.value.ToString();
    msg += System.Environment.NewLine;
}//for

System.Windows.Forms.MessageBox.Show(msg);

Resource:
Omitting all xsi and xsd namespaces when serializing an object in .NET?

The class files are attached below.

Member Avatar for lithium112

Thank you both very much for your replies!!! I currently went with tinstaafl's solution since it will get me moving quickly with the project that I am on. cgeier, all I have to say is wow! That is one of the most thorough tutorials I have seen on a forum. Thank you! I have never used serialization before but have wanted to look into it. And now you've given me an exelent tutorial to get started. :) Many thanks guys!

Member Avatar for lithium112

Oh, and one thing I had to change is instead of .Attribute("value").Value .Value had to be changed to .SetValue, otherwise it would throw an error.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.