Parsing XML using C#, getting attribute value - c#

I have the following (simplified) notepad file, which from what I understand is XML text:
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="Active_01" value="1">
</add>
</appSettings>
I am trying to parse it using C#.
So far I have the following:
public class RFIDScanner
{
public void GetScannerConfigFile()
{
string File = ConfigurationManager.AppSettings["RFIDScannerConfiguration"];
XmlDocument doc = new XmlDocument();
doc.Load(File);
XmlNode node = doc.DocumentElement.SelectSingleNode("/appSettings");
String nodename = node.Name;
}
}
So far I know this is all correct, as:
nodename = appSettings
Which is as it should be.
My problem is, how do I retrieve the value "1" from the field "Active_01".
I now know that the node "add" is a child of the node "appSettings", and a trying to work out how to get the value stored in it.

Not sure if you wish to parse the '1' from the key Active_01, or get the 1 from the value. In Any case, you can use the following code:
public void GetScannerConfigFile()
{
string File = ConfigurationManager.AppSettings["RFIDScannerConfiguration"];
XmlDocument doc = new XmlDocument();
doc.Load(File);
var yourFile = doc.DocumentElement;
if (yourFile == null) return;
// Here you'll get the attribute key: key = Active_01 - which can be simply parsed for getting only the "1"
string key = yourFile.ChildNodes[0].Attributes["key"].Value;
// Here you'll get the number "1" from the value attribute.
string value = yourFile.ChildNodes[0].Attributes["value"].Value;
}

There are many ways, one is to use the XPath using the function you use above:
var value = doc.DocumentElement.SelectSingleNode("/appSettings/add/#value");
Another option is to use xml serialization:
You'd define classes as follows:
public class add
{
[XmlAttribute]
public string key;
[XmlAttribute]
public int value;
}
public class appSettings
{
public add add;
}
And then deserialize as follows:
var ser = new XmlSerializer(typeof(appSettings));
var xmlReader = new XmlTextReader(new StringReader(s));
appSettings settings = (appSettings) ser.Deserialize(xmlReader);
xmlReader.Close();
You can then get the values from settings

I have used this way in my code to add values to xml
may be it will help
var surveyTypeList = new XElement("SurveyTypes");
foreach (var item in modelData.SurveyTypeList)
{
if (item.IsSelected)
{
var surveyType = new XElement("SurveyType");
var id = new XElement("Id");
id.Add(item.Value);
// **var i= id.Value**
surveyType.Add(id);
surveyTypeList.Add(surveyType);
}
}
you can get value by var i= id.Value in your code;

Related

C# - Can I select the value of an XML attribute by searching for a second attribute?

I want to select the value of the "data" attribute where the "key" attribute matches a specific search.
The XML file will look like this.
<connections>
<production>
<connection key="KEY1" data="value1" />
<connection key="KEY2" data="value2" />
<connection key="KEY3" data="value3" />
</production>
</connections>
So is there a way to return the value of the data attribute by searching for key = key1 for example?
You could do something like this if you wanted to avoid using XPath.
using System.Xml.Linq;
var xmlStr = File.ReadAllText("Testfile.xml");
XElement root = XElement.Parse(xmlStr);
var data = root.Element("production")
.Elements().Where(x => x.Attribute("key").Value == "KEY1")
.Select(x=>x.Attribute("data").Value)
.First();
Console.WriteLine(data);
Try this, using System.Xml you can read the xml file in question and iterate through the nodes to look for your value.
private void YOUR_METHOD_NAME()
{
// Call GetXml method which returns the result in the form of string
string result = GetXml("xmlfile.xml", "KEY2");
Debug.WriteLine(result);
}
private string GetXml(string filePath, string searchKey)
{
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlNodeList nodes = doc.SelectSingleNode("//connections/production").ChildNodes;
string output = string.Empty;
foreach (XmlNode node in nodes)
{
if (node.Attributes["key"].Value == searchKey)
{
output = node.Attributes["data"].Value;
}
else
{
continue;
}
}
return output;
}

How to generate tags inside another tag in XML c#?

I have a class file like below :
public class property : root
{
public string languages { get; set; }
}
I am trying to generate xml like below :
Final Output:
<root>
<property>
--other properties
<languages>
<en>This is English Languages description</en>
<fr></fr>
</languages>
</property>
</root>
This is how I am trying to generate the <languages> tag :
private string GenerateLanguageTag(IList<Languages> languages)
{
string lang = string.Empty;
foreach (var item in languages)
{
lang += "<" + item.IsoLanguageCode + ">" + item.Description + "</" + item.IsoLanguageCode + ">";
}
return lang;
}
output:
<root>
<property>
--other properties
<languages><en>This is English Languages description
</en><fr></fr></languages>
</property>
</root>
Code:
root root = GetData(data);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(root));
using (StringWriter xmlWriter = new StringWriter())
{
xmlSerializer.Serialize(xmlWriter, root);
value = xmlWriter.ToString();
value = value.Replace(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", "");
value = value.Replace(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", "");
value = value.Replace("utf-16", "ISO-8859-1");
if (File.Exists(filePath))
{
var document = XDocument.Parse(value);
document.Save(filePath);
}
}
Update:
Tags "en", "fr" and many other languages inside <languages></languages> are generated dynamically based on the languages we have in the database.
Rather than declaring languages as a string, declare it as an XElement and mark it with [XmlAnyElement("languages")]. This informs the serializer that the children of the languages property should be inserted as children of their parent <property> element. Thus your data model should look like:
public class root
{
public property property { get; set; }
}
public class property
{
[XmlAnyElement("languages")]
public XElement languages { get; set; }
}
And you would construct your model as follows:
// Your dynamic list of languages & values
var languages = new List<(string IsoLanguageCode, string Description)>
{
("en", "This is English Languages description"),
("fr", ""),
};
var root = new root()
{
property = new()
{
languages = new XElement("languages", languages.Select(l => new XElement(l.IsoLanguageCode, l.Description))),
},
};
Notes:
The documentation for XmlAnyElementAttribute indicates it should be applied to properties of type XmlElement or XmlNode (or arrays of the same), but in fact it works for properties of type XElement as well. Since LINQ-to-XML is easier to work with than the old XmlDocument API, I suggest using it instead.
In your question you show property as a subclass of root. In order to get the nesting you require, it should be a separate class contained by root, not a subclass of root.
To eliminate the xsi and xsd namespaces (without needing to do a string replacement) see XmlSerializer: remove unnecessary xsi and xsd namespaces.
Demo fiddle here.
Using Xml Linq :
string header = "<root></root>";
XDocument doc = XDocument.Parse(header);
XElement root = doc.Root;
root.Add( new XElement("property", new object[] {
new XElement("languages", new object[] {
new XElement("en", "This is English Languages description"),
new XElement("fr")
})
}));
}

Parse XML issue

I am parsing a huge XML using for-loops,SelectNodes,Attributes.GetNamedItem etc.
I came across an issue of how to parse the identical nodes CustLoyalty that are identical as shown in the abstract below. The issue is how to get the identical noded values since they are not exclusively inside a parent node
<Customer>
<PersonName>
<NamePrefix>Ms</NamePrefix>
<GivenName>Fra</GivenName>
<Surname>etti</Surname>
</PersonName>
<Telephone FormattedInd="false" PhoneLocationType="6" PhoneNumber="10" PhoneTechType="1"/>
<Telephone FormattedInd="false" PhoneLocationType="6" PhoneNumber="49" PhoneTechType="3"/>
<Email DefaultInd="true" EmailType="1">z#z</Email>
<Address Type="1">
<AddressLine>alace</AddressLine>
<StateProv StateCode="NY"/>
<CountryName Code="GB"/>
</Address>
<CustLoyalty MembershipID="3" ProgramID="Guest"/>
<CustLoyalty MembershipID="6" ProgramID="Freq"/>
<CustLoyalty MembershipID="56" ProgramID="teID"/>
<CustLoyalty MembershipID="N6" ProgramID="ID"/>
</Customer>
My code goes something like that:
XmlNodeList CustomerList = ProfileList[v].SelectNodes("df:Customer", mgr);
for (int w = 0; w < CustomerList.Count; w++)
{
XmlNodeList PersonNameList = CustomerList[w].SelectNodes("df:PersonName", mgr);
for (int x = 0; x < PersonNameList.Count; x++)
{
XmlNode NamePrefixNode = PersonNameList[x].SelectSingleNode("df:NamePrefix", mgr);
string NamePrefix = NamePrefixNode.InnerText;
XmlNode GivenNameNode = PersonNameList[x].SelectSingleNode("df:GivenName", mgr);
string GivenName = GivenNameNode.InnerText;
XmlNode SurnameNode = PersonNameList[x].SelectSingleNode("df:Surname", mgr);
string Surname = SurnameNode.InnerText;
myProfiles.GivenName = GivenName;
myProfiles.Surname = Surname;
myProfiles.NamePrefix = NamePrefix;
}
XmlNode TelephoneNode = CustomerList[w].SelectSingleNode("df:Telephone", mgr);
if (TelephoneNode != null)
{
string PhoneNumber = TelephoneNode.Attributes.GetNamedItem("PhoneNumber").Value;
myProfiles.Telephone = PhoneNumber;
}..........
Let's say that you are parsing it with XDocument object. Beware that XDocument can throw exception if your input isn't valid html and element xCostumer can have null value if element with name "Customer" is not in xDoc on top level in element hierarchy.
XDocument xDoc = XDocument.Parse(YourStringHoldingXmlContent);
XElement xCustomer = xDoc.Element("Customer");
foreach (XElement CustLoayalty in xCustomer.Elements("CustLoyalty"))
{
Console.WriteLine(CustomLoaylty.Value.ToString());
}
you can do the following
1- you define a class CustomLoyalty
public class CustomLoyalty
{
public string Membership{get;set;}
public string Program{get;set;}
}
2- declare a list call it uniqueCustomLoyalty
private List<CustomLoyalty> uniqueCustomLoyalty=new List<CustomLoyalty>();
3- while you are looping on the custom loyalty for each customer do this
foreach(var data in customLoyaltiesList)
{
// customLoyaltiesList is the list of nodes of type custom loyalty
// assume that the current object of customloyalty called detail
CustomLoyalty detail=new CustomLoyalty(){
Membership=data.Attributes.GetNamedItem("MembershipID").Value, // the code to get the value of membership ID according to the method you are using
Program=data.Attributes.GetNamedItem("ProgramID").Value,
};
// check if the list contains the current customloyalty
var exists=uniqueCustomLoyalty.FirstOrDefault(t=>MemberShip=detail.MemberShip && t.Program=detail.Program);
if(exists==null) // list doesn't contain this data
uniqueCustomLoyalty.Add(detail); // add the detail to the list to compare it with the rest of the parsing data
else{
// the data is not unique, you can do what ever you want
}
}
hope this will help you
regards

Change XML node with same name?

everyone!
I have an XML file and need to change the value of a node, specifically the indicated line. The problem i have is that as you can see, there are many nodes.
How can i change this line? This XML file could be much larger, so i am looking for a solution that would take different amounts of 'launch.file' nodes into account.
The node that will need to be set to True will be identified by the corresponding NAME tag. So if i typed in ULTII, the DISABLED node for that block will be set to True. If i typed in Catl, then the DISABLED node for that block would be changed.
<?xml version="1.0" encoding="windows-1252"?>
<SBase.Doc Type="Launch" version="1,0">
<Descr>Launch</Descr>
<Filename>run.xml</Filename>
<Disabled>False</Disabled>
<Launch.ManualLoad>False</Launch.ManualLoad>
<Launch.File>
<Name>Catl</Name>
<Disabled>False</Disabled>
<ManualLoad>False</ManualLoad>
<Path>ft\catl\catl.exe</Path>
</Launch.File>
<Launch.File>
<Disabled>False</Disabled> <!-- change to True -->
<ManualLoad>False</ManualLoad>
<Name>ULTII</Name>
<Path>F:\ULTII.exe</Path>
<NewConsole>True</NewConsole>
</Launch.File>
<Launch.File>
<Name>ECA</Name>
<Disabled>False</Disabled>
<Path>C:\ECA.exe</Path>
</Launch.File>
</SBase.Doc>
I am using Visual Studio 2012, should you need to know.
Thank you to anyone who can help me out on this, i really appreciate it.
Heres my method to do what you want
private void DisableLaunchFile(string xmlfile, string launchFileName){
XDocument doc = XDocument.Load(xmlfile);
var launchFileElement = doc.Descendants("Launch.File").Where (d => d.Element("Name").Value == lauchFileName);
launchFileElement.Elements("Disabled").First().Value = true.ToString();
doc.Save(xmlfile);
}
Use it like:
string pathToXmlFile = //assign ;
DisableLaunchFile(pathToXmlFile, "Catl");
DisableLaunchFile(pathToXmlFile, "ULTII");
This can be achieved by using LINQ to XML (see XDocument Class).
Assuming that there is the single Launch.File element with Name element with value "ULTII":
var document = XDocument.Load(...);
var ultiiElement = document
.Descendants("Launch.File")
.Single(fileElement => fileElement.Element("Name").Value == "ULTII");
ultiiElement.Element("Disabled").Value = "True"; // or true.ToString()
document.Save(...);
This method will do the trick:
public void ChangeNode(string name, string filePath)
{
XDocument xDocument;
using (var streamReader = new StreamReader(filePath))
{
xDocument = XDocument.Parse(streamReader.ReadToEnd());
}
var nodes = xDocument.Descendants("Launch.File");
foreach (var node in nodes)
{
var nameNode = node.Descendants("Name").FirstOrDefault();
if (nameNode != null && nameNode.Value == name)
{
var disabledNode = node.Descendants("Disabled").FirstOrDefault();
if (disabledNode != null)
{
disabledNode.SetValue("True");
}
}
}
using (var streamWriter = new StreamWriter(filePath))
{
xDocument.Save(streamWriter);
}
}
The name you want to pass in is the name of the node that you want to change and the path is the file path to the xml file. So you might call it like:
ChangeNode("ULTII", "C:\\output.xml");
You may need to tidy this up a bit like matching the node name invariant of case or culture but it should get you started.

How to retain XML string as a string field during XML deserialization

I got an XML input string and want to deserialize it to an object which partially retain the raw XML.
<SetProfile>
<sessionId>A81D83BC-09A0-4E32-B440-0000033D7AAD</sessionId>
<profileDataXml>
<ArrayOfProfileItem>
<ProfileItem>
<Name>Pulse</Name>
<Value>80</Value>
</ProfileItem>
<ProfileItem>
<Name>BloodPresure</Name>
<Value>120</Value>
</ProfileItem>
</ArrayOfProfileItem>
</profileDataXml>
</SetProfile>
The class definition:
public class SetProfile
{
public Guid sessionId;
public string profileDataXml;
}
I hope the deserialization syntax looks like
string inputXML = "..."; // the above XML
XmlSerializer xs = new XmlSerializer(typeof(SetProfile));
using (TextReader reader = new StringReader(inputXML))
{
SetProfile obj = (SetProfile)xs.Deserialize(reader);
// use obj ....
}
but XMLSerializer will throw an exception and won't output < profileDataXml >'s descendants to "profileDataXml" field in raw XML string.
Is there any way to implement the deserialization like that?
If you want to deserialize that as XML, then use an XML type (either XElement or XmlElement should work) - see code below.
public class StackOverflow_11234676
{
const string XML = #"<SetProfile>
<sessionId>A81D83BC-09A0-4E32-B440-0000033D7AAD</sessionId>
<profileDataXml>
<ArrayOfProfileItem>
<ProfileItem>
<Name>Pulse</Name>
<Value>80</Value>
</ProfileItem>
<ProfileItem>
<Name>BloodPresure</Name>
<Value>120</Value>
</ProfileItem>
</ArrayOfProfileItem>
</profileDataXml>
</SetProfile>";
public class SetProfile
{
public Guid sessionId;
public XElement profileDataXml;
}
public static void Test()
{
string inputXML = XML;
XmlSerializer xs = new XmlSerializer(typeof(SetProfile));
using (TextReader reader = new StringReader(inputXML))
{
SetProfile obj = (SetProfile)xs.Deserialize(reader);
Console.WriteLine(obj.profileDataXml);
}
}
}
I would say that you can deserialize this XML.
Take a look at this article:
Attributes That Control XML Serialization
Easiest way to get it working is to use REVERSE approach. Create classes and apply xml serialization attributes and experiment with serialization until you get the same xml result as posted. Once you get the same xml, your deserialization will work.
I would use Xml.Linq for this.
setProfile obj = new setProfile();
var doc = XDocument.Parse(yourXml);
obj.sessionID = doc.Root.Element("sessionID").value;
obj.profileDataXml = doc.Root.Element("profileDataXml").value;

Categories