1

I am using XML reader to search through an xml configuration file we use. I want to find the values under a certain key and be able to change them or add if they don't exist.

XML Sample

<DBSimulatorConfigurations>
  <Configurations>
    <DBSimulatorConfiguration>
      <Key>Test1</Key>
      <Submit>0</Submit>
      <Amend>0</Amend>
      <Update>0</Update>
      <Delete>1</Delete>
    <ResponseTimeInSeconds>100</ResponseTimeInSeconds>
    </DBSimulatorConfiguration>
    <DBSimulatorConfiguration>
      <Key>Test2</Key>
      <Submit>0</Submit>
      <AutoUpdate>0</AutoUpdate>
      <Amend>0</Amend>
      <Update>0</Update>
      <Delete>1</Delete>
    <ResponseTimeInSeconds>100</ResponseTimeInSeconds>
    </DBSimulatorConfiguration>
    <DBSimulatorConfiguration>
  </Configurations> 
</DBSimulatorConfigurations>

Code so far... commented out bit doesn't find the value within 'Test1'

XmlReader reader = XmlReader.Create("C:\\<path>\\DBConfigs.xml");
while(reader.Read())
{
    if (reader.ReadToDescendant("Key"))
    {
        reader.Read();
        if (reader.Value == "Test1")
        {
            Console.WriteLine("Found_1 {0}", reader.Value);
            // Doesn't work :( 
            // reader.Read();
            //if (reader.ReadToDescendant("Submit")) {
            //  Console.WriteLine("Value for Submit is {0}", reader.Value);
            //}
        }

        if (reader.Value == "Test2")
        {
            Console.WriteLine("Found_2 {0}", reader.Value);
        }

        reader.Read(); //this moves reader to next node which is text 
        if (reader.ReadToDescendant("Full.2")) {
            Console.WriteLine("Found {0}", reader.Value);
        }

    }

But what I want to do then is find and change the value for 'Submit' or 'Amend' etc or if there is no entry 'Submit' then i'll add one.

But first i'd like to be able to find and change the values for Test1.

John
  • 787
  • 4
  • 11
  • 28
  • 1
    Can't you use LINQ to XML and its [SetElement method](https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xelement.setelementvalue?view=netframework-4.7)? – Siva Gopal Jul 31 '17 at 08:10
  • If you are modifying your XML, it's going to be much, much easier to use LINQ to XML to do so. See [Linq to XML - update/alter the nodes of an XML Document](https://stackoverflow.com/q/331502/3744182) or [Update XML with C# using Linq](https://stackoverflow.com/q/1487653) to get started. – dbc Jul 31 '17 at 08:11
  • If you really, really want to do streaming transforms of XML using just `XmlReader` and `XmlWriter` you might start with [Combining the XmlReader and XmlWriter classes for simple streaming transformations](https://blogs.msdn.microsoft.com/mfussell/2005/02/12/combining-the-xmlreader-and-xmlwriter-classes-for-simple-streaming-transformations/). – dbc Jul 31 '17 at 08:15

2 Answers2

1

This appears to work to find value and update it. I've updated it so I can find any value for a particular value.

//search for  all nodes with <DBSimulatorConfiguration> element
string xml = "<path>.DBConfig.xml";
XDocument xdoc = XDocument.Load(xml);
var elements = xdoc.Root.Elements().Elements().Where(x => x.Name == "DBSimulatorConfiguration");
//iterate through all those eleemnt

foreach (var element in elements)
{
    //Console.WriteLine("Empty {0}", element.Value);
    //now find it's child named Submit
    var configKey = element.Elements().FirstOrDefault(x => x.Name == "Key");
    var configSubmit = element.Elements().FirstOrDefault(x => x.Name == "Submit");
    var configAmend = element.Elements().FirstOrDefault(x => x.Name == "Amend");
    var configUpdate = element.Elements().FirstOrDefault(x => x.Name == "Update");
    var configDelete = element.Elements().FirstOrDefault(x => x.Name == "Delete");

    //if such element is found
    if (configSubmit != null)
    {
        if (configKey.ElementsBeforeSelf().Any(x => x.Name == "Key" && x.Value == "Test1"))
        {
            Console.WriteLine("Found Key for Test1 {0}", configKey.Value);
        }
        if (configSubmit.ElementsBeforeSelf().Any(x => x.Name == "Key" && x.Value == "Test1"))
        {
            configSubmit.Value = "1";
            Console.WriteLine("Submit value is updated to {0}", configSubmit.Value);        
        }
        if (configAmend.ElementsBeforeSelf().Any(x => x.Name == "Key" && x.Value == "Test1"))
        {
            Console.WriteLine("Amend value is: {0}", configAmend.Value);
        }
        if (configUpdate.ElementsBeforeSelf().Any(x => x.Name == "Key" && x.Value == "Test1"))
        {
            Console.WriteLine("Update value is: {0}", configUpdate.Value);
        }
    }
}
xdoc.Save("<path>.DBConfig.xml");

Is this the most efficient way of doing this?

John
  • 787
  • 4
  • 11
  • 28
0

Here is one approach, using XDocument:

 string xml = @"
<DBSimulatorConfigurations>
  <Configurations>
    <DBSimulatorConfiguration>
      <Key>Test1</Key>
      <Submit>0</Submit>
      <Amend>0</Amend>
      <Update>0</Update>
      <Delete>1</Delete>
    <ResponseTimeInSeconds>100</ResponseTimeInSeconds>
    </DBSimulatorConfiguration>
    <DBSimulatorConfiguration>
      <Key>Test2</Key>
      <Submit>0</Submit>
      <AutoUpdate>0</AutoUpdate>
      <Amend>0</Amend>
      <Update>0</Update>
      <Delete>1</Delete>
    <ResponseTimeInSeconds>100</ResponseTimeInSeconds>
    </DBSimulatorConfiguration>
    <DBSimulatorConfiguration>
    </DBSimulatorConfiguration>
  </Configurations> 
</DBSimulatorConfigurations>";

XDocument xdoc = XDocument.Parse(xml);
//search for  all nodes with <DBSimulatorConfiguration> element
var elements = xdoc.Root.Elements().Elements().Where(x => x.Name == "DBSimulatorConfiguration");
//iterate through all those eleemnt
foreach (var element in elements)
{
    //now find it's child named Submit
    var submitElement = element.Elements().FirstOrDefault(x => x.Name == "Submit");
    //if such element is found
    if (submitElement != null)
    {
        //here you can change Submit element, like this:
        // submitElement.Value = "abc";
        //or you can check for something
        if (submitElement.ElementsBeforeSelf().Any(x=> x.Name == "Key" && x.Value== "Test2"))
        {
            //this is submitElement which is after element named Key with value Test2
            submitElement.Value = "some specific value";
        }
    }
    else
        element.Add(new XElement("Submit", 999));
}
Nino
  • 6,931
  • 2
  • 27
  • 42
  • I am getting an error with code. "System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1." The first lines of the xml file are: – John Jul 31 '17 at 12:14
  • @john your xml is not valid. XDocument expects valid xml. In fact, xml sample you provided in question was not valid either, it was missing closing `DBSimulatorConfigurations` node which I added. Xml in my answer should be valid and you can test code with it. To check your xml, you can use some of online tools like [this one](http://www.xmlvalidation.com/) or [this one](https://codebeautify.org/xmlvalidator) – Nino Jul 31 '17 at 12:22
  • Both tools you listed below say my xml is valid. But I solved the issue by using XDocument.Load instead of XDocument.Parse – John Jul 31 '17 at 14:16
  • @John well, did my answer help you? – Nino Jul 31 '17 at 14:43
  • yes, thank you it was about 90% what i was looking for. I've added my final solution, but i can't tick my own solution as an answer for another few hours. So i'll just tick your answer. – John Aug 01 '17 at 09:44