Getting data from xml with parent node details - c#

I have below xml file
<Department Name="Electronic">
<Employee ID="1" Name="John" Address="xyz,abc" />
<Employee ID="2" Name="Jim" Address="ntg,abc"/>
<Employee ID="3" Name="Liz" Address="rhx,abc" />
</Department>
<Department Name="Computer">
<Employee ID="1" Name="Tony" Address="mnc,abc"" />
<Employee ID="2" Name="Tom" Address="abr,abc" />
</Department>
I want to get data in this form using C# and linq
Electronic, 1 , John
Electronic, 2 , Jim
Electronic, 3 , Liz
Computer,1,Tony
Computer,2,Tom
Currently i am only getting ID and Name with below query, but i want to get department name as well
1 , John
2 , Jim
3 , Liz
1,Tony
2,Tom
var result1 = str.Elements("Department").Elements("Employee")
.Select(node => new
{
ID = node.Attribute("ID").Value,
Name = node.Attribute("Name").Value,
}
).ToList();

public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Department { get; set; }
}
//Replace doc with your xml element
XElement doc = XElement.Parse(File.ReadAllText("XmlFilePath"));
var employeeList = doc.Elements("Department").Elements("Employee")
.Select(node => new Employee
{
Id = node.Attribute("ID")?.Value,
Name = node.Attribute("Name")?.Value,
Address = node.Attribute("Address")?.Value,
Department = node.Parent?.Attribute("Name")?.Value,
}).ToList();

Try following which give the output you are looking for
using System;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApp2
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(INPUT_FILENAME);
StreamWriter writer = new StreamWriter(OUTPUT_FILENAME);
foreach(XElement department in doc.Descendants("Department"))
{
string departmentName = (string)department.Attribute("Name");
foreach(XElement employee in department.Elements("Employee"))
{
string id = (string)employee.Attribute("ID");
string employeeName = (string)employee.Attribute("Name");
string line = string.Join(",", new string[] { departmentName, id, employeeName });
writer.WriteLine(line);
}
}
writer.Flush();
writer.Close();
}
}
}

Related

How to Mapping Deserialize XML document using c#

i am new to c # programming and i m stuck in how ot Deserialize this XML document, i have seen this tutorial How to Deserialize XML document and it was helpful but as you can see my XML contains more informations and he is more complex :
<?xml version="1.0" encoding="utf-8"?>
<Classrooms>
<Classroom name="ClassroomA">
<Students name = "John"/>
<Students name = "Bryan"/>
<Students name = "Eva"/>
</Classroom>
<Classroom name="ClassroomB">
<Students name = "Harry"/>
<Students name = "Emma"/>
<Students name = "Joe"/>
</Classroom>
<Classroom name="ClassroomC">
<Students name = "Lionnel"/>
<Students name = "Rio"/>
<Students name = "Eva"/>
</Classroom>
</Classrooms>
My main goal is to create a Map of classrooms from my XML file :
example : Dictionnary<string,List> classrooms ,
Key 1 : classroomA, Values : John,Bryan,Eva
Key 2 : classroomB, Values : Harry,Emma,Joe
Key 3 : classroomC, Values : Lionnel,Rio,Eva
Thanks for help
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Linq;
namespace ConsoleApplication178
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Classrooms));
Classrooms classrooms = (Classrooms)serializer.Deserialize(reader);
reader.Close();
//using xml linq
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, List<string>> dict = doc.Descendants("Classroom")
.GroupBy(x => (string)x.Attribute("name"), y => y)
.ToDictionary(x => x.Key, y => y.Elements("Students").Select(x => (string)x.Attribute("name")).ToList());
}
}
public class Classrooms
{
[XmlElement()]
public List<Classroom> Classroom { get; set; }
}
public class Classroom
{
[XmlAttribute]
public string name { get; set; }
[XmlElement()]
public List<Students> Students { get; set; }
}
public class Students
{
[XmlAttribute]
public string name { get; set; }
}
}

Display field from XML Response Data using C# Console

I have below xml response data. Now i need to display ProjectName and MigrationStatus using C# console Application. I am new to C# please guide me to get fied names from xml node list. I have tried below code but confuisng a lot.
Thanks in advance
XML Response Code:
<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="https://URL"
<id><<URL>></id>
<title type="text">Projects</title>
<updated>2020-06-07T06:24:57Z</updated>
<link rel="self" title="Projects" href="Projects" />
<entry>
<id><<URL>>(guid'86c492af-8c63-ea11-b119-00155d6c5103')</id>
<category term="ReportingData.Project" scheme="<<URL>>" />
<link rel="edit" title="Project" href="Projects(guid'86c492af-8c63-ea11-b119-00155d6c5103')" />
<title />
<updated>2020-06-07T06:24:57Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:NOde1>BULLL</d:ProjectName>
<d:NOde2>DOM</d:NOde2>
</m:properties>
</content>
</entry>
</feed>
Try following xml linq to parse file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement feed = doc.Root;
XNamespace ns = feed.GetDefaultNamespace();
XNamespace nsM = feed.GetNamespaceOfPrefix("m");
XNamespace nsD = feed.GetNamespaceOfPrefix("d");
List<Entry> entries = doc.Descendants(ns + "entry").Select(x => new Entry()
{
id = (string)x.Element(ns + "id"),
title = (string)x.Element(ns + "title"),
updated = (DateTime)x.Element(ns + "updated"),
author_name = (string)x.Descendants(ns + "name").FirstOrDefault(),
ProjectName = (string)x.Descendants(nsD + "ProjectName").FirstOrDefault(),
MigrationStatus = (string)x.Descendants(nsD + "MigrationStatus").FirstOrDefault()
}).ToList();
Dictionary<string, Entry> dict = entries
.GroupBy(x => x.ProjectName, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
foreach (KeyValuePair<string, Entry> entry in dict)
{
Console.WriteLine(entry.Key);
}
}
}
public class Entry
{
public string id { get; set; }
public string title { get; set; }
public DateTime updated { get; set; }
public string author_name { get; set; }
public string ProjectName { get; set; }
public string MigrationStatus { get; set; }
}
}

Searching for an attribute by its value

I have following XML file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<Communication Id ="456">
<Person> Ivan Ivanov </Person>
<Describtion>
<Age> 16 </Age>
<Place> Moscow </Place>
<Key Name ="Language"> English </Key>
<Key Name ="Profession"> Doctor </Key>
</Describtion>
</Communication>
<Communication Id ="1010">
<Person> Petr Petrov </Person>
<Describtion>
<Age> 21 </Age>
<Place> St.Peterburg </Place>
<Key Name ="Language"> Français </Key>
<Key Name ="Profession"> Ingeneer </Key>
</Describtion>
</Communication>
</root>
There is a list of Key tags with attribute Name which has different value. This value determines which variable the value between tags will be written to. How can I write an algorithm for such a search?
You can search like this.
xmlDoc.SelectNodes("root/Communication/Describtion/Key[#Name=\"Language\"][text()=\" English \"]")
Try with XmlDocument
public static string getNodeValue(XmlDocument xmldoc, string id, string key)
{
return xmldoc.SelectSingleNode($"root/Communication[#Id='{id}']/Describtion/Key[#Name='{key}']")
.InnerText
.Trim();
}
Usage
var xmlDoc = new XmlDocument();
xmlDoc.Load(xmlFile);
Console.WriteLine(getNodeValue(xmlDoc, "456", "Language"));
Console.WriteLine(getNodeValue(xmlDoc, "1010", "Language"));
Output
English
Français
I used Xml Linq along with a dictionary and IEquatable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
StreamReader reader = new StreamReader(FILENAME);
reader.ReadLine(); //allow unicode characters
XDocument doc = XDocument.Load(reader);
List<Person> people = doc.Descendants("Communication").Select(x => new Person()
{
id = (int)x.Attribute("Id"),
name = (string)x.Element("Person"),
age = (int)x.Descendants("Age").FirstOrDefault(),
place = (string)x.Descendants("Place").FirstOrDefault(),
language = ((string)x.Descendants("Key").Where(y => (string)y.Attribute("Name") == "Language").FirstOrDefault()).Trim(),
profession = ((string)x.Descendants("Key").Where(y => (string)y.Attribute("Name") == "Profession").FirstOrDefault()).Trim()
}).ToList();
Dictionary<Person, List<Person>> dict = people
.GroupBy(x => x, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
List<Person> results = dict[new Person() { language = "English", profession = "Doctor" }].ToList();
}
}
public class Person : IEquatable<Person>
{
public int id { get; set; }
public string name { get; set; }
public int age { get; set; }
public string place { get; set; }
public string language { get; set; }
public string profession { get; set; }
public Boolean Equals(Person other)
{
return
(language == other.language) && (profession == other.profession);
}
public override int GetHashCode()
{
return (language + "^" + profession).GetHashCode() ;
}
}
}

Why am I getting first node again on second iteration in this XDocument

I read about XML XDocument and running a few test but now I get the same first node on second lap in the foreach
Here´s my C# code for the CRUD get:
public IActionResult GetBookItems()
{
List<BookItem> BookItems = new List<BookItem>();
XDocument doc = _db.GetXmlDb();
foreach (XElement element in doc.Root.Descendants())
{
BookItem bookitem = new BookItem
{
/// Id
/// Author
/// Title
/// Genre
/// Price
/// Publish_date
/// Description
Id = element.Attribute("id").Value,
Author = element.Element("author").Value,
Title = element.Element("title").Value,
Genre = element.Element("genre").Value,
Price = element.Element("price").Value,
Publish_date = element.Element("publish_date").Value,
Description = element.Element("description").Value
};
BookItems.Add(bookitem);
BookItems = BookItems.OrderBy(p => p.Title).ToList();
}
return Ok(BookItems);
}
Here´s the xml databas
<?xml version="1.0"?>
<catalog>
<book id="B1">
<author>Kutner, Joe</author>
<title>Deploying with JRuby</title>
<genre>Computer</genre>
<price>33.00</price>
<publish_date>2012-08-15</publish_date>
<description>Deploying with JRuby is the missing link between enjoying JRuby and using it in the real world to build high-performance, scalable applications.</description>
</book>
<book id="B2">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description>
</book>
</catalog>
Also the program crash at second lap when doing new BookItem
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=BooksProject
StackTrace:
at WorkSampleBookSearch.BooksXmlController.GetBookItems() in L:\NetProject\BooksProject\BooksProject\Controllers\BooksXmlController.cs:line 34
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__12.MoveNext()
Descendants will get both the parent and all the child nodes of each element so you will get each element multiple times like book with child Author and then get Author a second time. See code below for solution :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<BookItem> bookitems = doc.Descendants("book").Select(x => new BookItem() {
Id = (string)x.Attribute("id"),
Author = (string)x.Element("author"),
Title = (string)x.Element("title"),
Genre = (string)x.Element("genre"),
Price = (decimal)x.Element("price"),
Publish_date = (DateTime)x.Element("publish_date"),
Description = (string)x.Element("description")
}).ToList();
}
}
public class BookItem
{
public string Id { get; set; }
public string Author { get; set; }
public string Title { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
public DateTime Publish_date { get; set; }
public string Description { get; set; }
}
}

XML array with custom attribute and element names

I want to serialize/deserialize xml document in C# like:
<library>
<my.books genre =""classic"">
<book title = ""1984"" author=""George Orwell"" />
<book title = ""Robinson Crusoe"" author=""Daniel Defoe"" />
<book title = ""Frankenstein"" author=""Mary Shelly"" />
</my.books>
</library>";
There are 2 important things:
Element "my.books" must have custom name (not a property name)
my.books element must have an attribute ("genre").
Here is my code (sample is on https://dotnetfiddle.net/bH5WVX) :
using System;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Serialization;
using System.IO;
public class Program
{
public static void Main()
{
Library lib = new Library(myBooks: new MyBooks(
genre: "classic",
booklist: new List<Book>{
new Book("1984", "George Orwell"),
new Book("Robinson Crusoe", "Daniel Defoe"),
new Book("Oliver Twist", "Mary Shelly"),
}));
XmlSerializer formatter = new XmlSerializer(typeof(Library));
using (StringWriter sw = new StringWriter())
{
formatter.Serialize(sw, lib);
Console.Write(sw.ToString());
}
string desiredOutput =
#"<library>
<my.books genre =""classic"">
<book title = ""1984"" author=""George Orwell"" />
<book title = ""Robinson Crusoe"" author=""Daniel Defoe"" />
<book title = ""Frankenstein"" author=""Mary Shelly"" />
</my.books>
</library>";
}
[XmlRoot("library")]
public class Library
{
public MyBooks MyBooks { get; set; }
[XmlElement("my.books")]
public List<Book> Books { get; set; }
public Library()
{
}
public Library(MyBooks myBooks = null)
{
MyBooks = myBooks;
}
}
[XmlType("my.books")]
public class MyBooks
{
[XmlAttribute("genre")]
public string Genre { get; set; }
[XmlElement("book")]
public List<Book> Booklist { get; set; }
public MyBooks(string genre, List<Book> booklist = null)
{
Genre = genre;
Booklist = booklist;
}
public MyBooks()
{
}
}
public class Book
{
[XmlAttribute("title")]
public string Title { get; set; }
[XmlAttribute("author")]
public string Author { get; set; }
public Book() { }
public Book(string title, string author)
{
Title = title;
Author = author;
}
}
}
And the output is:
<library>
<MyBooks genre="classic">
<book title="1984" author="George Orwell" />
<book title="Robinson Crusoe" author="Daniel Defoe" />
<book title="Oliver Twist" author="Mary Shelly" />
</MyBooks>
</library>
The only problem is that I can't force element "MyBooks" to use name "my.books"
I found only one related article on this topic - http://www.codemeit.com/xml/c-xmlserializer-add-an-attribute-to-an-array-element.html, it suggests to use "XmlType" attribute on class, but it doesn't work here.
Is there any way to apply custom name attribute on this element?
It looks like your attribute was on the wrong property.
Try this:
[System.Xml.Serialization.XmlElement("my.books")]
public MyBooks MyBooks { get; set; }
public List<Book> Books { get; set; }
I now get this output:
<?xml version="1.0" encoding="utf-16"?>
<library xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<my.books genre="classic">
<book title="1984" author="George Orwell" />
<book title="Robinson Crusoe" author="Daniel Defoe" />
<book title="Oliver Twist" author="Mary Shelly" />
</my.books>
</library>
Well done on a superbly written question!

Categories