0

I've tried searching for the solution before asking it myself but I haven't found what I was looking for.

<bookstore>


<book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>

In the example above, say I want to change the text vaule "Harry Potter" to something else, what is the simplest way to do it

Yoni10014
  • 3
  • 3
  • Read up on XDocument (Linq to xml) and/or XmlDocument. http://stackoverflow.com/questions/1542073/xdocument-or-xmldocument – Jompa234 Jan 01 '17 at 16:04

2 Answers2

0

XDocument and LINQ to XML gives you a very expressive way of doing this.

// Parse our xml into an XDocument, which is a tree of nodes and collections of XElements, which represent xml elements
var xdoc = XDocument.Parse(
@"<bookstore>
  <book category=""cooking"">
     <title lang=""en"">Everyday Italian</title>
     <author>Giada De Laurentiis</author>
     <year>2005</year>
     <price>30.00</price>
  </book>
  <book category=""children"">
      <title lang=""en"">Harry Potter</title>
      <author>J K. Rowling</author>
      <year>2005</year>
      <price>29.99</price>
  </book>
  </bookstore>");

var book = xdoc
           // Start with the root node (bookstore)
           .Root
           // Take the child elements (books)
           .Elements()
           // Take 1, and only 1 book element such that it contains a title element with the value "Harry Potter"
           .Single(x => x.Element("title").Value == "Harry Potter");

// Set this books title element to the new title
book.Element("title").SetValue("NEW TITLE");
// Any changes we make alters the underlying XDocument, so we can print the final result.
Console.Write(xdoc.ToString());

For more info, read chapter 10 of C# 6.0 in a Nutshell.

Stuart
  • 5,358
  • 19
  • 28
  • I'm rather new with using C#, and even newer to the concept of xml files. If you can be more descriptive I would appreciate it – Yoni10014 Jan 02 '17 at 07:38
  • @Yoni10014 sure, I've added some comments, if there is anything that is unclear let me know and I'd be happy to elaborate. – Stuart Jan 02 '17 at 10:10
0

This is another way of achieving what you are after. A lot more code than what Stuart has provided. But I prefer this method as this is type safe and will stop you enter text in a number field.

#region Usings

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

#endregion

public class Program
{
    /// <summary>
    /// This method reads an xml file.
    /// Finds the element to change.
    /// Changes the Title.
    /// And Finally saves the changes back to the xml file.
    /// </summary>
    public void Main()
    {
        // The file path of the XML document.
        string filePath = "document.xml";

        // Initializing the book store variable.
        BookStore bookStore = null;

        // Initializing the reading and writing of the XML file.
        using (StreamReader fileReadStream = new StreamReader(filePath))
        using (StreamWriter fileWriteStream = new StreamWriter(filePath))
        {
            // Initializing the serializer.
            XmlSerializer serializer = new XmlSerializer(typeof(BookStore));

            // De-serializing the XML file to objects.
            bookStore = (BookStore)serializer.Deserialize(fileReadStream);

            // Finding the required element.
            Book book = bookStore.Book.Single(b => b.Category.Equals("children") &&
                                                   b.Title.Lang.Equals("en") &&
                                                   b.Title.Value.Equals("Harry Potter"));

            // Setting the new title of the book.
            book.Title.Value = "New Title";


            // Saving the changes back to the XML file.
            serializer.Serialize(fileWriteStream, bookStore);
        }
    }
}

#region BookStore Classes

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class BookStore
{
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("book")]
    public IEnumerable<Book> Book { get; set; }

}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class Book
{
    /// <remarks/>
    public BookStoreBookTitle Title { get; set; }

    /// <remarks/>
    public string Author { get; set; }

    /// <remarks/>
    public ushort Year { get; set; }

    /// <remarks/>
    public decimal Price { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string Category { get; set; }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class BookStoreBookTitle
{
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string Lang { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlTextAttribute()]
    public string Value { get; set; }
}

#endregion

Hope this helps.

  • Agreed, this is more ceremony, but gives you type safety provided you maintain the boilerplate (I'm used to F# where there is no need to compromise). If memory usage is important then with either method you could read the xml with an `XmlReader` and write with `XmlWriter`, you would then just serialize the book class (one at a time), not the full object graph, same for the linq sample. This lets you stream through, so for large, unbounded workloads, or if you just want to conserve memory that will be worth looking at. – Stuart Jan 01 '17 at 18:11
  • Cheers @Stuart i'll have to look in to using xmlReader and xmlWriter as would reduce the memory allocation. i'll have to play around with this and see what the performance difference is. – Gavin Harrison Jan 02 '17 at 13:32