Searching for an attribute by its value - c#

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() ;
}
}
}

Related

Getting data from xml with parent node details

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();
}
}
}

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; }
}
}

Setting a XML Element Name and XML Attribute to a Class Property

I have this Class that I want to convert to an XML Element
public class Person
{
[XmlElement(ElementName = "PersonName")]
public string Name { get; set; }
}
This will display an XML
<Person>
<PersonName>Smith</PersonName>
</Person>
I want to add an attribute to the element PersonName
<Person>
<PersonName required="true">Smith</PersonName>
</Person>
How would I do that?
I think you need a special class to hold your Name property, rather than relying on string. Here is an example, using the XmlText and XmlAttribute attributes to control how the built-in XmlSerializer works:
using System.Xml.Serialization;
using System.IO;
namespace SomeNamespace
{
class Program
{
static void Main(string[] args)
{
Person me = new Person("me");
string path = "C:\\temp\\person.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Person));
using (StreamWriter sw = new StreamWriter(path))
{
serializer.Serialize(sw, me);
}
}
}
public class Person
{
public Person() { } // needed for serialization
public Person(string name)
{
Name = new PersonName(name);
}
[XmlElement(ElementName = "PersonName")]
public PersonName Name { get; set; }
}
public class PersonName
{
public PersonName() { } // needed for serialization
public PersonName(string name)
{
Name = name;
}
[XmlText]
public string Name { get; set; }
[XmlAttribute] // serializes as an Attribute
public bool Required { get; set; } = true;
}
}
output (at C:\temp\person.xml; you can change Main to serialize to string and print to console if you want):
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PersonName Required="true">me</PersonName>
</Person>
If you really want your Required attribute to be serialized as the lowercase "required", you can use different properties of XmlAttribute, such as: XmlAttribute(AttributeName = "required")

How do Idesign a generic class to deserialize this XML?

I am trying to write classes for deserializing an XML response from some API.
Here is a sample response. When I query for ObjectA,
<Response>
<Status>0</Status>
<Message>Ok</Message>
<Data>
<ObjectAs>
<Count>2</Count>
<ObjectA>...</ObjectA>
<ObjectA>...</ObjectA>
</ObjectAs>
</Data>
</Response>
When I query for ObjectB,
<Response>
<Status>0</Status>
<Message>Ok</Message>
<Data>
<ObjectBs>
<Count>1</Count>
<ObjectB>...</ObjectB>
</ObjectBs>
</Data>
</Response>
I am trying to create a generic Response class, but everything I tried seems to be futile.
I can't change the response structure. And I am trying to avoid writing a new Response class for each of the response type.
Notice how under Data, each API response is different. For ObjectA it is <ObjectAs> and for ObjectB it is <ObjectBs>.
Here is my ApiResponse class,
[XmlRoot(ElementName = "Response")]
public class ApiResponse<T>
{
public int Code { get; set; }
public string Message { get; set; }
[XmlAnyElement("Data")]
public XmlElement DataElement { get; set; }
[XmlIgnore]
public List<T> Data
{
get
{
{
return null; // How do I parse and return the list of Objects (A, B, C, whatever?)
}
}
}
}
Here is my sample API XML response, when I query for devices.
<Response>
<Code>0</Code>
<Message>OK</Message>
<Data>
<Devices>
<Count>2</Count>
<Device>
<Name>0001</Name>
<Active>TRUE</Active>
<DeviceType>1</DeviceType>
<Address>192.168.0.75</Address>
<Port>80</Port>
<Memo/>
</Device>
<Device>
<Name>0002</Name>
<Active>TRUE</Active>
<DeviceType>1</DeviceType>
<Address>192.168.0.78</Address>
<Port>80</Port>
</Device>
</Devices>
</Data>
</Response>
And when I query for users,
<Response>
<Code>0</Code>
<Message>OK</Message>
<Data>
<Users>
<Count>1</Count>
<User>
<Name>Administrator</Name>
<Group>Admins</Group>
</User>
</Users>
</Data>
</Response>
Here is a generic solution using LINQ to XML that works:
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)
{
ApiResponse apiResponse = new ApiResponse(FILENAME);
}
}
public class ApiResponse
{
public int code { get; set; }
public string message { get; set; }
public string _type { get; set; }
public int count { get; set; }
public Dictionary<string, Dictionary<string, string>> dict { get; set; }
public ApiResponse(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement response = doc.Root;
code = (int)response.Element("Code");
message = (string)response.Element("Message");
XElement types = (XElement)response.Element("Data").FirstNode;
_type = types.Name.LocalName;
count = (int)types.Element("Count");
dict = types.Elements().Where(x => x.Name.LocalName != "Count")
.GroupBy(x => (string)x.Element("Name"), y => y.Elements()
.GroupBy(a => a.Name.LocalName, b => (string)b)
.ToDictionary(a => a.Key, b => b.FirstOrDefault()))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}

Categories