-4

I have an xml file, as per the sample below:

  <Header>
<CollectionDetails>
  <Collection></Collection>
  <Year>1415</Year>
  <FilePreparationDate></FilePreparationDate>
</CollectionDetails>
<Source>
  <ProtectiveMarking>PROTECT-PRIVATE</ProtectiveMarking>
  <UKPRN></UKPRN>
  <TransmissionType>A</TransmissionType>
  <SoftwareSupplier></SoftwareSupplier>
  <SoftwarePackage></SoftwarePackage>
  <Release>12.0.2.3</Release>
  <SerialNo>01</SerialNo>
  <DateTime>2015-10-22T17:01:51.800</DateTime>
</Source>

Within Header, I want to take out the "Year" value which is what i'm stuck on how to do. My current C# uses the below to read the file initially, but I can't get what I need from it so I can turn it into a string:

            XElement document = XElement.Load(str_FileLocation);
            var year = document.Element("Header");
            var year1 = year.Elements("CollectionDetails");
            var year2 = year1.Nodes("Year");
  • Could you clarify what you mean by "I want to take out the value"? It's unclear what you're trying to achieve here. (If you just want the entire content of the file as a string, just use `File.ReadAllText` instead of `XDocument.Load`...) – Jon Skeet Feb 20 '19 at 12:04
  • So sorry, I missed the word, Year, out. Amended the OP –  Feb 20 '19 at 12:17
  • 1
    Your code sample looks woefully short. – spodger Feb 20 '19 at 12:24
  • Once you have it in `doc`, you can take the `Root` property and programmatically descend to the `Year` node. A more flexible solution is to use XPath; check [this Stack Overflow post](https://stackoverflow.com/q/6209841/812149) for an explanation. – S.L. Barth is on codidact.com Feb 20 '19 at 12:26
  • Okay, so you need to find the right element. Have you done any research into that? I'd strongly recommend reading the Microsoft [LINQ to XML tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml) – Jon Skeet Feb 20 '19 at 12:26
  • simple : doc.Descendants("Year").Remove(); – jdweng Feb 20 '19 at 12:52
  • I have read up on a few bits, but I can't seem to get it to click. –  Feb 20 '19 at 13:08

2 Answers2

0

This is really straightforward with your XDocument:

XNamespace ns = "<default namespace here>"
var value = (string) doc.Descendants(ns + "Year").Single()

Where ns should be set to the default namespace as specified in the xmlns="..." attribute in an ancestor element (likely the document root).

I'd suggest reading the LINQ to XML guide for more explanation of how to use the API.

Charles Mager
  • 25,735
  • 2
  • 35
  • 45
  • Thanks for this. I get an error saying "System.InvalidOperationException: 'Sequence contains no elements'" though. –  Feb 20 '19 at 13:37
  • @voot89 You may need to give more detail of your XML (preferably edit the question to include a complete example). I suspect there is an `xmlns="..."` attribute (setting the default namespace) at the root. – Charles Mager Feb 20 '19 at 13:38
  • There is. yes. Does it matter as to the content? –  Feb 20 '19 at 13:41
  • @voot89 It will if you want a precise answer. I can amend to 'insert content here', otherwise. – Charles Mager Feb 20 '19 at 13:42
  • I am going to re-phrase my original question now. –  Feb 20 '19 at 14:14
  • Maybe that will change responses. –  Feb 20 '19 at 14:16
  • @voot89 The above should still work, if you include the correct namespace. I don't know what that is, because your question doesn't include the full XML content. – Charles Mager Feb 20 '19 at 14:17
  • It would be tricky to add that as the content is sensitive unfortunately. –  Feb 20 '19 at 14:18
  • Cracked it, I think. Seems you don't need the name itself, but if you use the set xElement i've now added ie: XElement document = XElement.Load(str_FileLocation); then do XNamespace year = document.Name.Namespace; it does it for you –  Feb 20 '19 at 14:23
  • @voot89 yep, that's possible. But given it's always going to be the same, why not specify it explicitly? – Charles Mager Feb 20 '19 at 15:06
-1

You can use following class to deserialize the xml into C# class object and then easily can access any property to get desired value

POC Code:

[XmlRoot(ElementName="CollectionDetails")]
public class CollectionDetails {
    [XmlElement(ElementName="Collection")]
    public string Collection { get; set; }
    [XmlElement(ElementName="Year")]
    public string Year { get; set; }
    [XmlElement(ElementName="FilePreparationDate")]
    public string FilePreparationDate { get; set; }
}

[XmlRoot(ElementName="Source")]
public class Source {
    [XmlElement(ElementName="ProtectiveMarking")]
    public string ProtectiveMarking { get; set; }
    [XmlElement(ElementName="UKPRN")]
    public string UKPRN { get; set; }
    [XmlElement(ElementName="TransmissionType")]
    public string TransmissionType { get; set; }
    [XmlElement(ElementName="SoftwareSupplier")]
    public string SoftwareSupplier { get; set; }
    [XmlElement(ElementName="SoftwarePackage")]
    public string SoftwarePackage { get; set; }
    [XmlElement(ElementName="Release")]
    public string Release { get; set; }
    [XmlElement(ElementName="SerialNo")]
    public string SerialNo { get; set; }
    [XmlElement(ElementName="DateTime")]
    public string DateTime { get; set; }
}

[XmlRoot(ElementName="Header")]
public class Header {
    [XmlElement(ElementName="CollectionDetails")]
    public CollectionDetails CollectionDetails { get; set; }
    [XmlElement(ElementName="Source")]
    public Source Source { get; set; }
}

Sample code:

XmlSerializer serializer = new XmlSerializer(typeof(Header));
StreamReader reader = new StreamReader(path);
var header = (Header)serializer.Deserialize(reader);
reader.Close();
//use the header object to access your data



Update Here's another way using xpath:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.XPath;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            XPathNavigator nav;
            XPathDocument docNav;
            string xPath;

            docNav = new XPathDocument(AppDomain.CurrentDomain.BaseDirectory + "test.xml");
            nav = docNav.CreateNavigator();
            xPath = "/Header/CollectionDetails/Year/text()";

            string value = nav.SelectSingleNode(xPath).Value;
            Console.WriteLine(value);
        }
    }
}
Nitin Sawant
  • 7,278
  • 9
  • 52
  • 98
  • 2
    I'd suggest this is *massive* overkill just to extra a single element value. It's really easy to do with LINQ to XML. – Jon Skeet Feb 20 '19 at 12:25