0

I am trying to use XElement to add new content to an existing XML data file.

For the most part it is working OK but I need to change it. This is what I have so far:

xdoc.Root.Add(
    new XElement("W" + record.Date.ToString("yyyyMMdd"),
        new XElement("Chairman", historyItem.Chairman),
        new XElement("AuxCounsellor1", historyItem.AuxCounsellor1),
        new XElement("AuxCounsellor2", historyItem.AuxCounsellor2),
        new XElement("VideoConferenceHost", string.Empty),
        new XElement("VideoConferenceCohost", string.Empty),
        new XElement("PrayerOpen", historyItem.PrayerOpen),
        new XElement("PrayerClose", historyItem.PrayerClose),
        new XElement("CBSConductor", historyItem.CBSConductor),
        new XElement("CBSReader", historyItem.CBSReader),
        new XElement("ReviewQuestion", string.Empty),
        new XElement("Items",
            new XAttribute("ItemCount", historyItem.TalkItems.Count),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[0].Name),
                new XElement("Theme", historyItem.TalkItems[0].Theme),
                new XElement("Method", historyItem.TalkItems[0].Method)),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[1].Name),
                new XElement("Theme", historyItem.TalkItems[1].Theme),
                new XElement("Method", historyItem.TalkItems[1].Method)),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[2].Name),
                new XElement("Theme", historyItem.TalkItems[2].Theme),
                new XElement("Method", historyItem.TalkItems[2].Method)),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[3].Name),
                new XElement("Theme", historyItem.TalkItems[3].Theme),
                new XElement("Method", historyItem.TalkItems[3].Method)),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[4].Name),
                new XElement("Theme", historyItem.TalkItems[4].Theme),
                new XElement("Method", historyItem.TalkItems[4].Method)),
            new XElement("Item",
                new XElement("Name", historyItem.TalkItems[5].Name),
                new XElement("Theme", historyItem.TalkItems[5].Theme),
                 new XElement("Method", historyItem.TalkItems[5].Method)))));

It creates an entry like this:

  <W20220404>
    <Chairman></Chairman>
    <AuxCounsellor1></AuxCounsellor1>
    <AuxCounsellor2></AuxCounsellor2>
    <VideoConferenceHost></VideoConferenceHost>
    <VideoConferenceCohost></VideoConferenceCohost>
    <PrayerOpen></PrayerOpen>
    <PrayerClose></PrayerClose>
    <CBSConductor></CBSConductor>
    <CBSReader></CBSReader>
    <ReviewQuestion></ReviewQuestion>
    <Items ItemCount="6">
      <Item>
        <Name></Name>
        <Theme>“Come essere un vero amico”</Theme>
        <Method>Talk</Method>
      </Item>
      <Item>
        <Name></Name>
        <Theme>Spiritual Gems</Theme>
        <Method>Question and Answers</Method>
      </Item>
      <Item>
        <Name></Name>
        <Theme>Prepare This Month's Presentations</Theme>
        <Method>Discussion with Video</Method>
      </Item>
      <Item>
        <Name></Name>
        <Theme>“Chi sono i tuoi amici online?”</Theme>
        <Method>Discussion with Video</Method>
      </Item>
      <Item>
        <Name></Name>
        <Theme>Diamo il benvenuto agli ospiti</Theme>
        <Method>Discussion with Video</Method>
      </Item>
      <Item>
        <Name></Name>
        <Theme></Theme>
        <Method>Talk</Method>
      </Item>
    </Items>
  </W20220404>

Now, I want to add another item into this W20220404 node, just after the list of Items. Eg:

<Teaching>
    <Name Class="0">Brother 9</Name>
    <Name Class="1">Brother 1</Name>
    <Name Class="2">Brother 3</Name>
</Teaching>

But:

  1. I only want to add the first Name if historyItem.Classes >= 1.
  2. I only want to add the second Name if historyItem.Classes >= 2.
  3. I only want to add the third Name if historyItem.Classes == 3.

I can't work out how to attach that. Initially I tried extending my code:

new XElement("Teaching",
    new XElement("Name",
        new XAttribute("Class", 0),
        new XText(historyItem.Teaching[0])),
    new XElement("Name",
        new XAttribute("Class", 1),
        new XText(historyItem.Teaching[1])),
    new XElement("Name",
        new XAttribute("Class", 2),
        new XText(historyItem.Teaching[2])))));

But it doesn't factor in for the logical requirements. What is the sensible way to add this node using the rules indicated?

To summarize:

Create the Teaching node
if Teaching is not null
    if historyItem.Classes >= 1
         Add the first Name / attribute
    if historyItem.Classes >= 2
         Add the second Name / attribute
    if historyItem.Classes == 3
         Add the third Name / attribute

So if the Teaching element is null I want an empty Teaching element in XML.

Making sense?

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

2 Answers2

1

You can take advantage of the fact that null nodes are ignored, so make a condition where you pass null when the condition is not met. Here is the basic idea:

new XElement("Teaching",
    historyItem.Classes >= 1 ? new XElement("Name",
        new XAttribute("Class", 0),
        new XText(historyItem.Teaching[0])) : null,

After reading the comment, it appears that you don't even need a conditional and could do the following:

historyItem.Teaching != null ? new XElement("Teaching",
    new XElement("Name",
        new XAttribute("Class", historyItem.Classes - 1),
        new XText(historyItem.Teaching[historyItem.Classes - 1]))
        ), null));
Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • Thanks. So will the `Teaching` element still be created (empty) if none of the `historyItem.Classes` tests are satisfied? – Andrew Truckle Jan 09 '22 at 22:56
  • 1
    Yes it will. If you do not want to create a teaching element, you need to make a condition for that as well `historyItem.Teaching != null ? new XElement("Teaching"...`. Also, I feel like you could use a loop to create these elements, but I have no idea what types of objects are `historyItem`, `Classes`, `Teaching`, so the answer is the best I could do. – Jesse Good Jan 09 '22 at 23:00
  • I am happy to do a loop, but it is not clear to me how to go about adding the element outside of the big chain I already have. Unless this loop can also be added to the chain too? `Classes` is `int` and `Teaching` is `List`. BTW. I **do** want an empty teaching element if the list is null. – Andrew Truckle Jan 09 '22 at 23:02
  • If I can understand this loop it would be good because that list of `Items` is also a `List` of 6 elements and could be simplified. – Andrew Truckle Jan 09 '22 at 23:05
  • @AndrewTruckle: I added another example. As far as I can tell, since `Classes` is an `int`, you simply need to subtract `1` to get the index in `Teaching`. – Jesse Good Jan 09 '22 at 23:14
  • Yes, but if Classes was 0 you would end up with -1. I will have to try tomorrow. – Andrew Truckle Jan 09 '22 at 23:19
  • @AndrewTruckle: I see. In that case you could just check if classes is between `1` and `3`. – Jesse Good Jan 09 '22 at 23:22
  • 1
    I also want to investigate the loop approach. I saw https://stackoverflow.com/a/21260096. For Items. I will update you tomorrow. Thanks. – Andrew Truckle Jan 09 '22 at 23:24
  • I have accepted your answer. Although I have added a second one that shows an alternative approach to acheive the same goal. – Andrew Truckle Jan 10 '22 at 08:07
0

I thought I would add this as an alternative to the initial answer which goes about things slightly differently. An alternative to using one large chain and factoring in the logic requirements for the Teaching node:

XElement historyWeek = new XElement("W" + record.Date.ToString("yyyyMMdd"));

if(historyItem.Meeting)
{
    historyWeek.Add(new XElement("Chairman", historyItem.Chairman),
        new XElement("AuxCounsellor1", historyItem.AuxCounsellor1),
        new XElement("AuxCounsellor2", historyItem.AuxCounsellor2),
        new XElement("VideoConferenceHost", string.Empty),
        new XElement("VideoConferenceCohost", string.Empty),
        new XElement("PrayerOpen", historyItem.PrayerOpen),
        new XElement("PrayerClose", historyItem.PrayerClose),
        new XElement("CBSConductor", historyItem.CBSConductor),
        new XElement("CBSReader", historyItem.CBSReader),
        new XElement("ReviewQuestion", string.Empty));

    XElement historyTalkItems = new XElement("Items",
        new XAttribute("ItemCount", historyItem.TalkItems.Count));
    foreach (var talkItem in historyItem.TalkItems)
    {
        historyTalkItems.Add(new XElement("Item",
                    new XElement("Name", talkItem.Name),
                    new XElement("Theme", talkItem.Theme),
                    new XElement("Method", talkItem.Method)));
    }
    historyWeek.Add(historyTalkItems);

    XElement historyTeaching = new XElement("Teaching");
    if(historyItem.Teaching.Any())
    {
        for (int iClass = 0; iClass < historyItem.Classes; iClass++)
        {
            historyTeaching.Add(new XElement("Name",
                new XAttribute("Class", iClass),
                new XText(historyItem.Teaching[iClass])));
        }
    }
    historyWeek.Add(historyTeaching);
}
xdoc.Root.Add(historyWeek);
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164