parsing an xml type string - c#

I have a string like this
<InitParams>
<myparams>
<Ad>true</Ad>
<Ay>true</Ay>
<Sd>false</Sd>
</myparams>
<myContent>
<Item>
<IM>true</IM>
<AL>1234</AL>
</Item>
</myContent>
</InitParams>
I need the value between the tags <IM> and <AL>. Being new to C# and .net not sure what would be the best way to do it. Read up on xmlDoc and linq but sounds like overkill for this small need.

The whole point of something like LINQ to XML was to prevent overkill, because it's so easy to use:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace WhateverNamespaceYouWant
{
public class Item
{
public bool IM { get; set; }
public int AL { get; set; }
}
public class ItemsRepository
{
public IEnumerable<Item> GetAllItemsInXML()
{
var _items = new List<Item>();
var doc = XDocument.Load("this");
// finds every node of Item
doc.Descendants("Item").ToList()
.ForEach(item =>
{
var myItem = new Item() // your domain type
{
IM = item.Element("IM").Value.ConvertToValueType<bool>(),
AL = item.Element("AL").Value.ConvertToValueType<int>(),
};
_items.Add(myItem);
});
return _items;
}
}
public static class Extensions
{
public static T ConvertToValueType<T>(this string str) where T : struct
{
try
{
return (T)Convert.ChangeType(str, typeof(T));
}
catch
{
return default(T);
}
}
}
}

Related

Cannot convert lambda expression to type because it is not a delegate type

I'm a beginner with C# and recently I've learned about the Breadth-First Search algorithm so I try to write it with C#. Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
public class Node
{
public string NodeName { get; set; }
public bool visited { get; set; }
public LinkedList<Node> _item;
public Node()
{
_item = new LinkedList<Node>();
}
public LinkedList<Node> Child { get { return _item; } set { _item = value; } }
}
public static class NodeExtension
{
public static void BFS(this Node root, string name)
{
Node result = n.Child.Find(x => x.NodeName == "name");
foreach(var item in n.Child)
{
item.visited = false;
}
Queue<Node> queue = new Queue<Node>();
root.visited = true;
queue.Enqueue(root);
while(queue.Any())
{
result = queue.First();
Console.WriteLine(result.NodeName);
queue.Dequeue();
}
LinkedList<Node> list = n.Child;
foreach(var v in list)
{
if(!v.visited)
{
v.visited = true;
queue.Enqueue(v);
}
}
}
}
But then I get an error message "Cannot convert lambda expression to type 'Node' because it is not a delegate type" from this line:
Node result = n.Child.Find(x => x.NodeName == "name");
Can someone help me explain the meaning of this message and how to solve it and if there is any problem with my function please also help me point that out, much appreciate it!

Getting parts of a repetitive string in C#

Extracting parts of a string is a common question, but here the string content, e.g. ' "main\":{\"temp\": ' is repetitive:
string test = "{\"cod\":\"200\",\"message\":0,\"cnt\":40,\"list\":[{\"dt\":1576810800,\"main\":{\"temp\":288.19,\"feels_like\":284.44,\"temp_min\":288.19,\"temp_max\":291.53,\"{\"dt\":1576821600,\"main\":{ \"temp\":283.97,\"feels_like\":281.56,\"temp_min\":283.97,\"temp_max\":286.47,\"pressure\":1007,\"sea_level\":1007,\"grnd_level\":997,\"humidity\":93,\"temp_kf\":-2.5},\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rai\",\"icon\":\"10d\"}],\"clouds\":{\"all\":90},\"wind\"";
Using
string s = test.Substring(test.IndexOf("\"main\":{\"temp\":") + 15);
I get
288.19,"feels_like":284.44,,"temp_min":288.19,"temp_max":291.53,"{"dt":157682160
0,"main":{ "temp":283.97,"feels_like":281.56,"temp_min":283.97,"temp_max":286.47
,"pressure":1007,"sea_level":1007,"grnd_level":997,"humidity":93,"temp_kf":-2.5}
,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],
"clouds":{"all":90},"wind"
instead of
288.19
Any clue what command would help? Should be something that can be adapted to ' "temp_max" ' or ' "pressure" '.
Cheers
Thanks to Mohammed, here's the working code spitting out all tempeatures:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using Newtonsoft.Json;
namespace ConsoleApp2
{
public class RootObject
{
public List<MyList> list { get; set; }
}
public class MyList
{
public Main main { get; set; }
}
public class Main
{
public double temp { get; set; }
}
class Program
{
static void Main()
{
using (WebClient client = new WebClient())
{
Console.WriteLine("ACCESSING ...");
string test = client.DownloadString("http://api.openweathermap.org/data/2.5/forecast?q=Auckland,NZ&APPID=45c3e583468bf450fc17026d6734507e");
//string test = "{\"cod\":\"200\",\"message\":0,\"cnt\":40,\"list\":[{\"dt\":1576810800,\"main\":{\"temp\":288.19,\"feels_like\":284.44,\"temp_min\":288.19,\"temp_max\":291.53,\"{\"dt\":1576821600,\"main\":{ \"temp\":283.97,\"feels_like\":281.56,\"temp_min\":283.97,\"temp_max\":286.47,\"pressure\":1007,\"sea_level\":1007,\"grnd_level\":997,\"humidity\":93,\"temp_kf\":-2.5},\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rai\",\"icon\":\"10d\"}],\"clouds\":{\"all\":90},\"wind\"";
var myobject = JsonConvert.DeserializeObject<RootObject>(test); //test is JSON response as string
foreach (var item in myobject.list)
{
var temp = item.main.temp;
Console.WriteLine(temp);
}
Console.ReadLine();
}
}
}
}
Using
var regexGroups = Regex.Matches(test, "\"main\" ?: ?{ ?\"temp\" ?: ?(\\d+\\.\\d+)")
.Select(x => x.Groups[1].Value);
You can get all \"main\":{\"temp\": values.
//classes.cs
public class RootObject
{
public List<MyList> list { get; set; }
}
public class MyList
{
public Main main { get; set; }
}
public class Main
{
public double temp { get; set; }
}
//API client
using Newtonsoft.Json;
var myobject = JsonConvert.DeserializeObject<RootObject>(test); //test is JSON response as string
foreach(var item in myobject.list){
var temp = item.main.temp;
}
//Hope it helps

Deserializing XML Based on Element Attributes

I am trying to deserialize an XML file within a C# program that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Addresses>
<ListName>Flowers</ListName>
<Address contextRef="RP.CC">Some Address</Address>
<Address contextRef="RP.BE">Some Other Address</Address>
<Address contextRef="RP.BV">Yet Another Address</Address>
<Address contextRef="RP.CAL">Wow, I Can't Believe It's Another Address</Address>
</Addresses>
I do not have any control over the format of this file. But, it will always have some combination of these 4 Address elements (i.e. these 4 contextRef attribute values are the only ones used) with differing element values each time.
Now, instead of deserializing into an Address array, I need to send them to individual properties within an Addresses object. My Current implementation uses an array and then a setter method to set these properties based on the contextRef as so:
public class Addresses
{
[XmlElement("ListName")]
public string ListName { get; set; }
private Address[] _addresses;
[XmlElement("Address")]
public Address[] AddressesArray
{
get
{
return _addresses;
}
set
{
_addresses = value;
SetAddress();
}
}
[XmlIgnore]
public Address AddressG21 { get; set; }
[XmlIgnore]
public Address AddressG22 { get; set; }
[XmlIgnore]
public Address AddressG23 { get; set; }
[XmlIgnore]
public Address AddressG9 { get; set; }
private void SetAddress()
{
foreach (var address in _addresses)
{
if (address.ContextRef == "RP.CC")
{
AddressG21 = address;
}
else if (address.ContextRef == "RP.BE")
{
AddressG22 = address;
}
else if (address.ContextRef == "RP.BV")
{
AddressG23 = address;
}
else if (address.ContextRef == "RP.CAL")
{
AddressG9 = address;
}
}
}
}
Where the Address object is defined as so:
public class Address
{
private string valueField;
/// <remarks/>
[XmlText]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
[XmlAttribute("contextRef")]
public string ContextRef { get; set; }
}
So, my question is, is there a neater/better way of deserializing this XML directly into the AddressG21, etc. object properties without first using the Address array?
Thanks in advance.
I would use xml linq and create a dictionary in the class
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);
Addresses addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
If you had multiple Addresses elements then use this
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<Addresses> addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}

Deserialize XML document, keeping track of order

I am parsing an XML document using the XMLSerialization tool. The sample XML file consists of paragraphs (string) and tables, which are a complex XML type. Tables consist of a series of row, which consists of a series of entry (string)
I need to keep track of the position of each table, relative to each paragraph. Is there a way of catching the position of each table as it is being parsed by the XMLSerialization tool? Or do I need to use a construct like [XMLAnyElement] and parse each paragraph and table sequentially in order to track the table position? I would prefer to avoid that approach, because my real XML files have many levels that would need manual parsing. I have a feeling that I am missing something really obvious here, but I've been scouring SO and trying multiple approaches, but with no straightforward solution.
Here is my basic code:
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace XMLDeserializeTest
{
class Program
{
static void Main(string[] args)
{
string file = Environment.CurrentDirectory + #"\test.xml";
test testClass = Deserialize(file);
}
static test Deserialize(string url)
{
XmlSerializer reader =
new XmlSerializer(typeof(test));
StreamReader stream = new StreamReader(url);
return reader.Deserialize(stream) as test;
}
}
public class test
{
[XmlElement("paragraph")]
public List<string> paragraphs { get; set; }
[XmlElement("table")]
public List<Table> tables { get; set; }
public test()
{
}
}
public class Table
{
[XmlElement("row")]
public List<Row> rows { get; set; }
public int nodeNumber { get; set; } // This is what needs to be tracked
public Table()
{
}
}
public class Row
{
[XmlElement("entry")]
public List<string> entries { get; set; }
public Row()
{
}
}
My sample XML:
<?xml version="1.0" encoding="utf-8" ?>
<test>
<paragraph>Here is some text.</paragraph>
<paragraph>Here is some more text. The table follows this paragraph.</paragraph>
<table>
<row>
<entry>1</entry>
<entry>2</entry>
<entry>3</entry>
</row>
<row>
<entry>4</entry>
<entry>5</entry>
<entry>6</entry>
</row>
</table>
<paragraph>This is the last paragraph. This comes after the table.</paragraph>
</test>
I came up with one solution, using XDocument, but it seems pretty clumsy:
XDocument Xdoc = XDocument.Load(file);
int numParagraphs = 0;
int tableNumber = 0;
foreach(XElement item in Xdoc.Root.Descendants())
{
if (item.Name.LocalName.Equals("paragraph"))
{
numParagraphs++;
}
else if (item.Name.LocalName.Equals("table"))
{
testClass.tables[tableNumber].nodeNumber = numParagraphs;
tableNumber++;
}
}
One option would just be to serialize the paragraph "index" of the table into your XML at the time of serialization. That way you wouldn't have to do anything custom.
However, to do what you are looking for with the XmlSerializer you could handle the deserialization yourself for certain element types using the UnknownElement event. Notice that the XmlElement attributes have been removed from the test class in order for the table and paragraph elements to be handled.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace XMLDeserializeTest
{
class Program
{
static int paragraphCount = 0;
static void Main(string[] args)
{
string file = Environment.CurrentDirectory + #"\test.xml";
paragraphCount = 0;
test testClass = Deserialize(file);
}
static test Deserialize(string url)
{
XmlSerializer serializer = new XmlSerializer(typeof(test));
serializer.UnknownElement += serializer_UnknownElement;
StreamReader stream = new StreamReader(url);
return serializer.Deserialize(stream) as test;
}
static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
test t = (test)e.ObjectBeingDeserialized;
if (e.Element.Name == "table")
{
var s = new XmlSerializer(typeof(Table));
var sr = new StringReader(e.Element.OuterXml);
Table newTable = s.Deserialize(sr) as Table;
newTable.nodeNumber = paragraphCount;
t.tables.Add(newTable);
}
else if (e.Element.Name == "paragraph")
{
String paragraphText = e.Element.InnerText;
t.paragraphs.Add(paragraphText);
paragraphCount++;
}
}
}
public class test
{
public List<string> paragraphs { get; set; }
public List<Table> tables { get; set; }
public test()
{
}
}
[Serializable, XmlRoot("table")]
public class Table
{
[XmlElement("row")]
public List<Row> rows { get; set; }
public int nodeNumber { get; set; } // This is what needs to be tracked
public Table()
{
}
}
[Serializable, XmlRoot("row")]
public class Row
{
[XmlElement("entry")]
public List<string> entries { get; set; }
public Row()
{
}
}
}

Error while implementing IEnumerator

I followed the article here and the sample code given in it.
What I am trying to implement is pretty straightfoward. I do have a fair understanding about collections and enumerators. However, what I don't understand is that even though there is hardly any difference in the way I have implemented the code as compared to how it is implemented in the given article, why I am getting an error.
Only difference in the implementation is that the sample code uses T (generic) whereas I am using a class named Address while implementing the custom Addresses collection class.
The code is pretty straightfoward. I have the following classes in the project.
Contact class
Addresses class (Implements custom collection and inherits from ICollection)
Address class
AddressEnumerator
What I wish to achieve is the Dataset like functionality where we can use a syntax like:
Dataset ds = new Dataset();
Ds.Tables[0]....blah blah blah.
I get a compile time error in the following method of the AddressEnumerator.cs class.
Error:
cannot apply indexing with [] to an expression of type ConsoleApplication2.Addresses (Addresses class implements an ICollection)
Compile time error occurs in the following statement:
_current = _collection[index];
public bool MoveNext()
{
if(++index >= _collection.Count)
{
return false;
}
else
{
_current = _collection[index];
}
return true;
}
Source code:
//following code snippet does not traverse the collection
foreach (Address a in c.Addresses)
{
Console.WriteLine(a.Street);
}
Program.cs
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//RenderTimeSheet();
Address ad = new Address();
ad.Street = "Hollywood";
ad.City = "LA";
ad.State = "California";
ad.ZipCode = "93494";
ad.Country = "USA";
using (Contact c = new Contact(ad))
{
c.FirstName = "John";
c.LastName = "Doe";
Console.WriteLine(c.FirstName);
Console.WriteLine(c.LastName);
foreach (Address a in c.Addresses)
{
Console.WriteLine(a.Street);
}
}
Console.ReadKey();
}
}
Contact.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
namespace ConsoleApplication2
{
public class Contact : IDisposable
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Addresses Addresses { get; set; }
public Contact(Address a)
{
Addresses = new Addresses(a);
}
public Contact()
{
}
public void Dispose()
{
Console.Write("Disposing off...");
}
}
}
Addresses.cs
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
public class Addresses : ICollection<Address>
{
private IList<Address> _lstAddress;
protected bool _IsReadOnly;
public Addresses(Address _a)
{
_lstAddress = new List<Address>();
}
public void Add(Address item)
{
_lstAddress.Add(item);
}
public void Clear()
{
_lstAddress.Clear();
}
public bool Contains(Address item)
{
foreach(Address a in _lstAddress)
{
if(a.Street == item.Street)
{
return true;
}
}
return false;
}
public void CopyTo(Address[] array, int arrayIndex)
{
throw new Exception("Not valid for this implementation.");
}
public int Count
{
get { return _lstAddress.Count; }
}
public bool IsReadOnly
{
get { return _IsReadOnly; }
}
public bool Remove(Address item)
{
bool result = false;
for (int i = 0; i < _lstAddress.Count; i++)
{
Address obj = (Address)_lstAddress[i];
if(obj.Street == item.Street)
{
_lstAddress.RemoveAt(i);
result = true;
break;
}
}
return result;
}
public IEnumerator<Address> GetEnumerator()
{
return new AddressEnumerator(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
//throw new NotImplementedException();
return this.GetEnumerator();
}
}
}
Address.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
}
AddressEnumerator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
public class AddressEnumerator : IEnumerator<Address>
{
protected Addresses _collection;
protected int index;
protected Address _current;
public AddressEnumerator()
{
}
public AddressEnumerator(Addresses collection)
{
_collection = collection;
index = -1;
_current = default(Address);
}
public Address Current
{
get
{
return _current;
}
}
public void Dispose()
{
_collection = null;
_current = default(Address);
index = -1;
}
object System.Collections.IEnumerator.Current
{
get
{
return _current;
}
}
public bool MoveNext()
{
if(++index >= _collection.Count)
{
return false;
}
else
{
_current = _collection[index];
}
return true;
}
public void Reset()
{
throw new NotImplementedException();
}
}
}
this is a direct and short solution to your problem,
but it is not a "complete clean" solution, also the coding style of the complete implementation should be changed. there are more effective ways implementing enumerable interfaces ...
change the line
_current = _collection[index];
to
_current = _collection._lstAddress[index];
but you also need to change the access modifier
private IList<Address> _lstAddress
for example to
internal IList<Address> _lstAddress
The reason that the sample code works and yours doesn't is because the sample code class BusinessObjectCollection includes this:
public virtual T this[int index]
{
get
{
return (T)_innerArray[index];
}
set
{
_innerArray[index] = value;
}
}
which provides the subscript operator [] that your code lacks.
If you add that to your Addresses class (changing _innerArray to _lstAddress) then it should work, I think.

Categories

Resources