Link to home
Start Free TrialLog in
Avatar of trevor1940
trevor1940

asked on

C#: Adding elements to XML file part2

Hi this is a follow on from here  and other questions relating to parsing XML with C#

I'm trying to add a new season to the xml

My CheckSeason fails where no season exists eg for "A Discovery Of Witches"  but returns true or false for any MacGyver season

I'm assuming it's because the parent "season" has to exist before you can check for season number?
I don't understand why this causes an "unhandled exception " and not just return null


AddSeason i've tried to amend from previous question  but i'm missing something

Logic
Add season if none exist
Add new season in numerical  SeasonNumber order


       public static XDocument xdoc = XDocument.Load(@"J:\Media\TV\TV.xml");
        // Load the XML Document using Linq to XML


            if (CheckSeason("A Discovery Of Witches", "S01", "77236"))  // fails
            //    if (CheckSeason("MacGyver 2016", "S02", "67133"))        // works
                {
                Console.WriteLine("Exists");
            }
            else
            {
                Console.WriteLine("Does Not Exists");
                Console.WriteLine("Add Season");
                AddSeason("A Discovery Of Witches", "S01", "77236");
            }


        private static void AddSeason(string showname, string season, string TMDBid)
        {
            char[] array1 = { 's', 'S', '0' };
            foreach (char c in array1)
            {
                season = season.TrimStart(c);
            }
            var NewSeason = new XElement("Season",
               new XElement("SeasonNumber", season));

            var showSeason = (from node in xdoc.Descendants("Show")
                        where node.Element("TMDBid").Value == TMDBid
                        select node).FirstOrDefault();


            if (showSeason == null)
            {
                // Get a reference to the Show element and add it to it.
                showSeason.AddAfterSelf(NewSeason);
                // Sort the Shows in the document
                //var showOrdered = (from shows in xdoc.Descendants("Show")
                //                   orderby shows.Element("Name").Value
                //                   select shows).ToList();

                //// Remove the unsorted shows
                //xdoc.Root.RemoveAll();
                //// Add the sorted shows
                //xdoc.Root.Add(showOrdered);

                // Save the document with the new Show
                xdoc.Save(@"J:\Media\TV\TVNew.xml");
            }



        }

        private static bool CheckSeason(string showname, string season, string TMDBid)
        {
            char[] array1 = { 's', 'S', '0' };
            foreach (char c in array1)
            {
                season = season.TrimStart(c);
            }
            // // Checks for the existence of a Show Season
            var results = (from node in xdoc.Descendants("Show")
                           where node.Element("TMDBid").Value == TMDBid &&
                                 node.Element("Season").Element("SeasonNumber").Value == season
                           select node).FirstOrDefault();
            return results != null ? true : false;
        }

Open in new window


TV.xml

<?xml version="1.0" encoding="utf-8"?>
<TV>
  <Show>
    <Name>A Discovery Of Witches</Name>
    <FolderName>A Discovery Of Witches</FolderName>
    <TMDBid>77236</TMDBid>
  </Show>
  <Show>
    <Name>MacGyver 2016</Name>
    <FolderName>MacGyver 2016</FolderName>
    <TMDBid>67133</TMDBid>
    <Season>
      <SeasonNumber>3</SeasonNumber>
      <Episode>
        <EpisodeNumber>1</EpisodeNumber>
        <EpisodeName>Improvise</EpisodeName>
        <BackDrop>https://image.tmdb.org/t/p/w350_and_h196_bestv2/eQMVJkBltvID73sh9vvTistssS6.jpg</BackDrop>
        <FullPath>J:\Media\TV\MacGyver (2016)\S03\Macgyver.2016.S03E01.Improvise.mp4</FullPath>
      </Episode>
    </Season>
  </Show>
</TV>

Open in new window

Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Yes, you're getting a null reference exception because the Season tag doesn't exist for that show. Your code assumes it exists here:

node.Element("Season").Element("SeasonNumber").Value

Since node.Element("Season") evaluates to null when there's no season, you end up running:

null.Element("SeasonNumber").Value

...which throws a null ref exception.

Honestly, you would be better off not trying to do everything directly from the XML. Just create model classes with your desired structures (including defaults so you can avoid errors like this) and use XML serialization to load data from the XML file and save it back to the XML file when you're done with changes.

It should perform better, lead to much easier and simpler code, and give you a LOT more flexibility overall.
Plus, the serialization stuff is really easy - the XML serializer does so much work for you, it only takes a handful of lines of code to serialize and deserialize.

And if you later need even better performance, you'll be in a good position to make a few tweaks and change your storage to something like Sqlite (keeping you portable). That way your sorting can all be done within the DB, you can use indexes for fast searches, and you can keep your code clean and focused on data view and entry.
Hi trevor1940;
 
In C# when you attempt to access an object that does not exist you will get the dreaded error "System.NullReferenceException: 'Object reference not set to an instance of an object.'". The reason why you are getting this error is that the query is trying to drill down into the XML document where and object does not exist. The following query does that
var results = (from node in xdoc.Descendants("Show")
               where node.Element("TMDBid").Value == TMDBid &&
                     node.Element("Season").Element("SeasonNumber").Value == season
               select node).FirstOrDefault();

Open in new window

In the above query you are asking for all Show nodes, this is not a problem because Show exist. In the where clause you are trying to see if a values exist but the value to which it would be attached to does not exist and that is an XElement. and so it throws an exception. Before attempting to accessing an object that may or may not exist test for its existence first as shown in the code snippet below.
var results = (from node in xxdoc.Descendants("Show")
               where node.Element("TMDBid") != null &&  
                     node.Element("TMDBid").Value == TMDBid && node.Element("Season") != null &&
                     node.Element("Season").Element("SeasonNumber") != null &&
                     node.Element("Season").Element("SeasonNumber").Value == season
               select node).FirstOrDefault();

Open in new window

Avatar of trevor1940
trevor1940

ASKER

Hi
@ gr8gonzo can you show me a tutorial on   serialize and deserialize XML?
FYI I'm doing this in order to learn how to manipulate XML because I know of a real task coming my way in the near future

@Fernando Soto

That makes sense

Taking this one step further adding a new season I've created the code bellow

        private static void AddSeason(string showname, string season, string TMDBid)
        {
            char[] array1 = { 's', 'S', '0' };
            foreach (char c in array1)
            {
                season = season.TrimStart(c);
            }
            var NewSeason = new XElement("Season",
               new XElement("SeasonNumber", season));

            var showSeason = (from node in xdoc.Descendants("Show")
                              where node.Element("TMDBid") != null &&
                                    node.Element("TMDBid").Value == TMDBid 
                              select node.Element("TMDBid")).FirstOrDefault();


            if (showSeason != null)
            {
                showSeason.AddAfterSelf(NewSeason);
                xdoc.Save(@"J:\Media\TV\TVNew.xml");
            }



        }

Open in new window



That adds the new season after the TMDBid which is OK for SeasonNumber 1  but not so good for SeasonNumber 2

I've tried to work out how to adjust above but I'm stuck

               showSeason.AddAfterSelf(NewSeason);
                // Sort the Shows Seasons in the document
                var SeasonOrdered = (from node in xdoc.Descendants("Show")
                                     where node.Element("TMDBid") != null &&
                                           node.Element("TMDBid").Value == TMDBid
                                     orderby node.Element("Season").Element("SeasonNumber")
                                     select node).ToList();
// results SeasonOrdered  = 1

Open in new window

Sure. So the basic idea can be seen in this barebones example of a Winforms app (create a new Winforms app, add the Models.cs class, and then pull in the appropriate code into your own form):

Models.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Trevor1940Example
{
    public class TV
    {
        public List<Show> Shows { get; set; } = new List<Show>();

        #region Loading/Saving
        private static string xmlFile = "TV.xml";

        public static TV Load()
        {
            if (!File.Exists(xmlFile)) { return new TV(); }

            XmlSerializer deserializer = new XmlSerializer(typeof(TV));
            using (TextReader reader = new StreamReader(xmlFile))
            {
                TV tv = (TV)deserializer.Deserialize(reader);
                return tv;
            }
        }

        public void Save()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(TV));
            using (TextWriter writer = new StreamWriter(xmlFile))
            {
                serializer.Serialize(writer, this);
            }
        }
        #endregion
    }

    public class Show
    {
        public string Name { get; set; }
        public string FolderName { get; set; }
        public int TMBDBid { get; set; }
        public List<Season> Season { get; set; } = new List<Season>();
    }

    public class Season
    {
        public int SeasonNumber { get; set; }
        public Episode Episode { get; set; }
    }

    public class Episode
    {
        public int EpisodeNumber { get; set; }
        public string EpisodeName { get; set; }
        public string BackDrop { get; set; }
        public string FullPath { get; set; }
    }



}

Open in new window


Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Trevor1940Example;

namespace WindowsFormsApp4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            TV tv = TV.Load();
            tv.Shows.Add(new Show()
            {
                Name = "MacGyver 2016",
                FolderName = "MacGyver 2016",
                TMBDBid = 67133,
                Season = new List<Season>()
                {
                    new Season()
                    {
                        SeasonNumber = 3,
                        Episode = new Episode()
                        {
                            EpisodeNumber = 1,
                            EpisodeName = "Improvise",
                            BackDrop = @"https://image.tmdb.org/t/p/w350_and_h196_bestv2/eQMVJkBltvID73sh9vvTistssS6.jpg",
                            FullPath = @"J:\Media\TV\MacGyver (2016)\S03\Macgyver.2016.S03E01.Improvise.mp4"
                        }
                    }
                }
            });
            tv.Shows.Add(new Show()
            {
                Name = "A Discovery Of Witches",
                FolderName = "A Discovery Of Witches",
                TMBDBid = 77236

            });
            foreach(Show show in tv.Shows)
            {
                // etc...
            }
            tv.Save();
        }
    }
}

Open in new window


That basic example, when you run it, should create a new XML file called TV.xml that has the following data:
<?xml version="1.0" encoding="utf-8"?>
<TV xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Shows>
    <Show>
      <Name>MacGyver 2016</Name>
      <FolderName>MacGyver 2016</FolderName>
      <TMBDBid>67133</TMBDBid>
      <Season>
        <Season>
          <SeasonNumber>3</SeasonNumber>
          <Episode>
            <EpisodeNumber>1</EpisodeNumber>
            <EpisodeName>Improvise</EpisodeName>
            <BackDrop>https://image.tmdb.org/t/p/w350_and_h196_bestv2/eQMVJkBltvID73sh9vvTistssS6.jpg</BackDrop>
            <FullPath>J:\Media\TV\MacGyver (2016)\S03\Macgyver.2016.S03E01.Improvise.mp4</FullPath>
          </Episode>
        </Season>
      </Season>
    </Show>
    <Show>
      <Name>A Discovery Of Witches</Name>
      <FolderName>A Discovery Of Witches</FolderName>
      <TMBDBid>77236</TMBDBid>
      <Season />
    </Show>
  </Shows>
</TV>

Open in new window



There's a little tweaking to be done (e.g. you'd probably just change the "TV" node to be the actual List<> instead of being an extra container, but you get the basic idea.

I bookmark and use this article as a cheat sheet, but you can find the same information in a dozen different places with a quick Google search on C# XML serialization examples:
https://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part

There's also a part 2 of the same article that contains the deserialization side of things.

That article will show you how to map XML to your classes/properties if you want to use a different name or store a value as an attribute or stuff like that. It's pretty simple stuff, though.
Hi trevor1940;

I have modified your code. Please read the comments to understand what I did.
private static void AddSeason(string showname, string season, string TMDBid)
{
    // This will do the same thing as declearing an array of char's and stripping then 
    // from the begining of the string one at a time
    season = season.TrimStart(new char[] { 's', 'S', '0' });

    // If you are going to create a new season you should create an Episode node to hold
    // all upcomming episodes. This way you will not need to check if an Episode node exist.
    var NewSeason = new XElement("Season",
       new XElement("SeasonNumber", season),
       new XElement("Episode"));

    var showSeason = (from node in xxdoc.Descendants("Show")
                      where node.Element("TMDBid") != null &&
                            node.Element("TMDBid").Value == TMDBid
                      // Grab the reference to the Show node and not the TMDBid node
                      // this will make handleing nodes of this show simpler
                      select node).FirstOrDefault();


    if (showSeason != null)
    {
        // Now to add it to the end of all the Seasons in the show we can do the following
        // showSeason gives you access to the correct show, grabbing the Elements("Season") will
        // get all the Season node then using Last() on that gets you the last Season then you can do 
        // AddAfterSelf(NewSeason) to place it in the end.
        showSeason.Elements("Season").Last().AddAfterSelf(NewSeason);
        xxdoc.Save(@"J:\Media\TV\TV3New.xml");
    }
}

Open in new window

Not had much time to test this today & may not until the weekend

@ gr8gonzo Your idea makes sense especially when adding multiple item like all tv episodes  in a season
Also thanx for the link there are many tutorials on the web it's hard to know which are good or bad


@Fernando Soto

Sorry your code produces

""Sequence contains no elements""

@ line 27 when no season exists
Hi Trevor,

Yeah, you gain a LOT when when you start moving your logic into individual classes rather than trying to have several chunks of code that are all trying to manipulate an XML file. Consider that you incur a little more overhead from the XML engine itself for everything you do within that XML document. For example, the LINQ code:
from node in xxdoc.Descendants("Show")

Open in new window

...you're making the XML engine work to go find those matching nodes before the LINQ code can take the next step. If you have everything pulled out of XML into a regular C# code structure / model, then there's virtually no overhead.

Granted, when you're working with 1 or 2 items, the difference can't be seen, but as data volume grows, all of the small inefficiencies begin to add up, and things that COULD be instantaneous are instead taking a couple seconds and making your CPU work harder for trivial things.

Plus, the organization of code is better. For example, you have this bit of code:
season = season.TrimStart(new char[] { 's', 'S', '0' });

Open in new window


...but you could easily put that into the Season class itself, so you're not muddying up the rest of the code with little formatting bits that are specific to certain classes/elements:
    public class Season
    {
        public int SeasonNumber { get; set; }
        public Episode Episode { get; set; }

        // Public parameterless constructor (needed for serialization)
        public Season() { }

        // Constructor that automatically parses the season number
        public Season(string seasonName)
        {
          if(seasonName.Length > 0)
          {
            int.TryParse(seasonName.TrimStart(new char[] { 's', 'S', '0' }), out SeasonNumber);
          }
        }
    }

Open in new window


Now you can just do something like:
Season season = new Season("S12"); // The season.SeasonNumber property will automatically be parsed to the number 12. 

Open in new window


As your code grows, the organization of code into the respective classes can help tremendously, especially if you need to go back and refactor  anything.

In any event, try to make use of the model approach and simply treat XML as nothing more but your portable data storage. I do understand the underlying motivation to learn how to manipulate XML but even in those cases, it's often better to use serialization/deserialization to approach manipulation of data.
Hi trevor1940;

If the node does not exist you need to add it before adding child nodes to it, see code snippet.
private static void AddSeason(string showname, string season, string TMDBid)
{
    // This will do the same thing as declearing an array of char's and striping then 
    // from the begining of the string one at a time
    season = season.TrimStart(new char[] { 's', 'S', '0' });

    // If you are going to create a new season you should create an Episode node to hold
    // all upcomming episodes. This way you will not need to check if an Episode node exist.
    var NewSeason = new XElement("Season",
       new XElement("SeasonNumber", season),
       new XElement("Episode"));

    var showSeason = (from node in xxdoc.Descendants("Show")
                      where node.Element("TMDBid") != null &&
                            node.Element("TMDBid").Value == TMDBid
                      // Grab the reference to the Show node and not the TMDBid node
                      // this will make handleing nodes of this show simpler
                      select node).FirstOrDefault();


    if (showSeason != null)
    {
        // First you will need to test to see if any existance of a Season if NOT do the else part of if otherwise
        // Now to add it to the end of all the Seasons in the showm we can do the followinf
        // showSeason gives you access to the correct show, grabbing the Elements("Season") will
        // get all the Season node then using Last() on that gets you the last Season then you can do 
        // AddAfterSelf(NewSeason) to place it in the end.
        if (showSeason.Element("Season") != null )
            showSeason.Elements("Season").Last().AddAfterSelf(NewSeason);
        else
            showSeason.LastNode.AddAfterSelf(NewSeason);
        xdoc.Save(@"TV3New.xml");
    }
}

Open in new window

It would be recommended that when you create a show you add all the necessary container nodes so that you will not need to test if it exist or not. The following function will do that.
private XElement CreateShow(string showname, string foldername, string TMDBid, string seasonno = "")
{
    var addShow = new XElement("Show",
            new XElement("Name", showname),
            new XElement("FolderName", foldername),
            new XElement("TMDBid", TMDBid),
            new XElement("Season",
                new XElement("SeasonNumber", seasonno),
                new XElement("Episode")));

    return addShow;

}

Open in new window

And will return the following.
<Show>
  <Name>Star Trek</Name>
  <FolderName>C:\Star-Trek</FolderName>
  <TMDBid>123456</TMDBid>
  <Season>
    <SeasonNumber></SeasonNumber>
    <Episode />
  </Season>
</Show>

Open in new window

When called like this
var newShow = CreateShow("Star Trek", @"C:\Star-Trek", "123456");

Open in new window

Hi
@ gr8gonzo I've been reading up on serialize and deserialize XML  admittedly I've not had the opportunity to test  this myself

1 thing I don't get  how do you query data once it has been read in?

For instance listing all the TV shows by name  or seeing if a new TV episode exists before adding  it?

In your example above  you do

public List<Show> Shows { get; set; } = new List<Show>();

Open in new window

Where as the serialize and deserialize  link suggest the internal data is XML

Dose this mean you can use LINQ quarries  the way  @Fernando Soto has shown
Well, you can still query via LINQ if you want. LINQ is generic, so it's not tied to anything specific to XML or anything.

XML is simply the storage format. When you deserialize from XML to classes, you're not dealing with XML anymore. You're dealing with just regular data types within .NET. When you serialize, you're recreating the XML to represent those data types.

If you do a LOT of checking to see if something exists, you might want to just maintain a separate lookup table like a Dictionary.
@Fernando Soto

I'm getting myself really confused and don't have the in depth knowledge to fix so I'm uploading what I have

The first block of code errors probably due to A Discovery Of Witches having empty holding elements has you suggested it should have

The code fails to find A Discovery Of Witches or add a new season / episode
 for MacGyver A new season is added but fails to add a new episode to that season

I know it is because the queries are failing but am unsure how to debug the query inorder to workout what it should be especially when something is suggesting it to be null

Input XML

<?xml version="1.0" encoding="utf-8"?>
<TV>
  <Show>
    <Name>A Discovery Of Witches</Name>
    <FolderName>A Discovery Of Witches</FolderName>
    <TMDBid>77236</TMDBid>
    <Season>
    <SeasonNumber></SeasonNumber>
    <Episode />
  </Season>
  </Show>
  <Show>
    <Name>MacGyver 2016</Name>
    <FolderName>MacGyver 2016</FolderName>
    <TMDBid>67133</TMDBid>
    <Season>
      <SeasonNumber>3</SeasonNumber>
      <Episode>
        <EpisodeNumber>1</EpisodeNumber>
        <EpisodeName>Improvise</EpisodeName>
        <BackDrop>https://image.tmdb.org/t/p/w350_and_h196_bestv2/eQMVJkBltvID73sh9vvTistssS6.jpg</BackDrop>
        <FullPath>J:\Media\TV\MacGyver (2016)\S03\Macgyver.2016.S03E01.Improvise.mp4</FullPath>
      </Episode>
    </Season>
  </Show>
</TV>

Open in new window


Entire C# file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;


namespace XmlReader
{
    class Program
    {   
       public static XDocument xdoc = XDocument.Load(@"J:\Media\TV\TV1.xml");
        // Load the XML Document using Linq to XML

        public static void Main(string[] args)
        {

            //  This causes null exception errows at 1st .ToList()
            // Query the Document for the Show's
            //var shows = (from node in xdoc.Descendants("Show")
            //             select new
            //             {
            //                 ShowName = node.Element("Name").Value,
            //                 Season = node.Elements("Season")
            //                              .Select(s => new
            //                              {
            //                                  SeasonNumber = s.Element("SeasonNumber").Value,
            //                                  Episode = s.Elements("Episode").Select(ep => new
            //                                  {
            //                                      EpisodeNumber = ep.Element("EpisodeNumber").Value,
            //                                      EpisodeName = ep.Element("EpisodeName").Value,
            //                                      BackDrop = ep.Element("BackDrop").Value,
            //                                      FullPath = ep.Element("FullPath").Value
            //                                  }).ToList()
            //                              })

            //             }).ToList();

            //foreach (var item in shows)
            //{
            //    Console.WriteLine("Show Name: {0}", item.ShowName);
            //    foreach (var season in item.Season)
            //    {
            //        Console.WriteLine("\tSeason Number: {0}", season.SeasonNumber);
            //        foreach (var ep in season.Episode)
            //        {
            //            Console.WriteLine("\t\tEpisodeNumber: {0}, EpisodeName: {1}, BackDrop: {2}, FullPath: {3} ",
            //                ep.EpisodeNumber, ep.EpisodeName, ep.BackDrop, ep.FullPath);
            //        }
            //    }
            //}

            //Console.ReadLine();
            Console.WriteLine("CheckSeason MacGyver ");
            Console.ReadLine();
            
            if (CheckSeason("MacGyver 2016", "S02", "67133"))
            {
                Console.WriteLine("Exists");
            }
            else
            {
                Console.WriteLine("Does Not Exists");
                Console.WriteLine("Add Season");
                AddSeason("MacGyver 2016", "S02", "67133");
                AddShow("MacGyver 2016", "2", "1", "67133");
            }



            if (CheckShow("MacGyver 2016", "3", "1"))
            {
                Console.WriteLine("Exists");
            }
            else
            {
                Console.WriteLine("Does Not Exists");
                Console.WriteLine("Add Show");
            }
            if (CheckShow("MacGyver 2016", "3", "2"))
            {
                Console.WriteLine("Exists");
            }
            else
            {
                Console.WriteLine("Does Not Exists MacGyver 3 2");
                Console.WriteLine("Add Show");
                AddShow("MacGyver 2016", "3", "2", "67133");
            }

            Console.WriteLine("CheckSeason A Discovery Of Witches");


            if (CheckSeason("A Discovery Of Witches", "S01", "77236"))
            {
                Console.WriteLine("Exists");
                // fails to locate 
            }
            else
            {
                Console.WriteLine("Does Not Exists");
                Console.WriteLine("Add Season A Discovery Of Witches S01");
                AddSeason("A Discovery Of Witches", "S01", "77236");
            }



            if (CheckShow("A Discovery Of Witches", "1", "1"))
            {
                Console.WriteLine("Exists");
            }
            else
            {
                Console.WriteLine("Does Not Exists A Discovery Of Witches S01E01");
                Console.WriteLine("Add Show");
                AddShow("A Discovery Of Witches", "S01", "1", "77236");
            }




            Console.ReadLine();
        }// end main

        private static void AddSeason(string showname, string season, string TMDBid)
        {
            // This will do the same thing as declearing an array of char's and striping then 
            // from the begining of the string one at a time
            season = season.TrimStart(new char[] { 's', 'S', '0' });

            // If you are going to create a new season you should create an Episode node to hold
            // all upcomming episodes. This way you will not need to check if an Episode node exist.
            var NewSeason = new XElement("Season",
               new XElement("SeasonNumber", season),
               new XElement("Episode"));

            var showSeason = (from node in xdoc.Descendants("Show")
                              where node.Element("TMDBid") != null &&
                                    node.Element("TMDBid").Value == TMDBid
                              // Grab the reference to the Show node and not the TMDBid node
                              // this will make handleing nodes of this show simpler
                              select node).FirstOrDefault();


            if (showSeason != null)
            {
                // First you will need to test to see if any existance of a Season if NOT do the else part of if otherwise
                // Now to add it to the end of all the Seasons in the showm we can do the followinf
                // showSeason gives you access to the correct show, grabbing the Elements("Season") will
                // get all the Season node then using Last() on that gets you the last Season then you can do 
                // AddAfterSelf(NewSeason) to place it in the end.
                if (showSeason.Element("Season") != null)
                    showSeason.Elements("Season").Last().AddAfterSelf(NewSeason);
                else
                    showSeason.LastNode.AddAfterSelf(NewSeason);
                // fails to add if A Discovery Of Witches<
                xdoc.Save(@"J:\Media\TV\TVNew.xml");
            }
        }

        private static bool CheckSeason(string showname, string season, string TMDBid)
        {
            season = season.TrimStart(new char[] { 's', 'S', '0' });

            // // Checks for the existence of a Show Season
            var results = (from node in xdoc.Descendants("Show")
                           where node.Element("TMDBid") != null &&
                                 node.Element("TMDBid").Value == TMDBid && node.Element("Season") != null &&
                                 node.Element("Season").Element("SeasonNumber") != null &&
                                 node.Element("Season").Element("SeasonNumber").Value == season
                           select node).FirstOrDefault();

            return results != null ? true : false;
        }

        private static void AddShow(string showname, string SeasonNumber, string episodeNumber, string TMDBid)
        {
            SeasonNumber = SeasonNumber.TrimStart(new char[] { 's', 'S', '0' });
            var episode = new XElement("Episode",
            new XElement("EpisodeNumber", episodeNumber),
            new XElement("EpisodeName", "Tick Tock"),
            new XElement("BackDrop", "https://image.tmdb.org/t/p/w350_and_h196_bestv2/eQMVJkBltvID73sh9vvTistssS67.jpg"),
            new XElement("FullPath", @"J:\Media\TV\MacGyver (2016)\S03\Macgyver.2016.S03E02.TickTock.mp4")
            );
            // Query the Document for the Show to add episode to
            var show = (from node in xdoc.Descendants("Show")
                        where node.Element("TMDBid") != null &&
                              node.Element("TMDBid").Value == TMDBid && node.Element("Season") != null &&
                              node.Element("Season").Element("SeasonNumber") != null &&
                              node.Element("Season").Element("SeasonNumber").Value == SeasonNumber
                        select node.Element("Season").Element("SeasonNumber")).FirstOrDefault();

            //var show = (from node in xdoc.Descendants("Show")
            //            where node.Element("TMDBid").Value == TMDBid &&
            // node.Element("Season").Element("SeasonNumber").Value == SeasonNumber
            //            select node.Element("Season").Element("SeasonNumber")).FirstOrDefault();



            // See if the Show was found and add episode if it was
            if (show != null)
            {
                // Add the episode
                show.AddAfterSelf(episode);
                // Save the document with the new episode
                xdoc.Save(@"J:\Media\TV\TVNew.xml");
            }


        } // End Add Show

        private static bool CheckShow(string showname, string season, string episode)
        {
            // // Checks for the existence of a Show Season and Episode
            var results = (from node in xdoc.Descendants("Show")
                           where node.Element("Name").Value == showname &&
                                  node.Element("Name").Value != null &&
                                 node.Element("Season").Element("SeasonNumber").Value == season 
                                 && node.Element("Season") != null &&
                                 node.Element("Season").Element("SeasonNumber") != null &&
                                 node.Element("Season").Element("Episode") != null &&
                                 node.Element("Season").Element("Episode").Element("EpisodeNumber").Value != null &&
                                  node.Element("Season").Element("Episode").Element("EpisodeNumber").Value == episode
                           select node).FirstOrDefault();
            return results != null ? true : false;
        }

        internal void AddShowName(string Name, string FolderName, string TMDBid, XDocument Xdoc)
        {
            var NewShow = new XElement("Show",
                new XElement("Name", Name),
                new XElement("FolderName", FolderName),
                new XElement("TMDBid", TMDBid)
                );

            var show = (from node in Xdoc.Descendants("Show")
                        where node.Element("FolderName").Value == FolderName
                        select node).FirstOrDefault();


            if (show == null)
            {
                // Get a reference to the Root element and add it to it.
                Xdoc.Root.Add(NewShow);
                // Sort the Shows in the document
                var showOrdered = (from shows in Xdoc.Descendants("Show")
                                   orderby shows.Element("Name").Value
                                   select shows).ToList();

                // Remove the unsorted shows
                Xdoc.Root.RemoveAll();
                // Add the sorted shows
                Xdoc.Root.Add(showOrdered);

                // Save the document with the new Show
                Xdoc.Save(@"J:\Media\TV\TVNew.xml");
            }

        }


    }
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I'm not totally sure  I'm doing this correctly even in the context of this learning how C# processes XML I've solved 1 problem only to introduce others
For example
adding child container nodes stops the need for  testing null nodes but leaves these empty child nodes in final XML

I  really  appreciate both of your help and guidance

Thank you
I'm assuming you're referring to empty nodes like:
<Episode />
or
<Season />

If so, it's -usually- a good idea (in my opinion) to have empty containers than null values. Not only does it explicitly enforce a structure in the XML itself (which is good practice), but if you're ever manually looking for data quality issues in the raw XML via a text editor, it's easier to search for empty containers like <Season /> since you can't really search for something that's not there at all. Additionally, when you work with empty containers (instead of null values), you don't have to worry about null checks - you can assume there's always a container and all you have to do is check the Count property of the container in your code.

That said, if you change empty containers to null before serialization, you won't get those empty nodes at all.