Deserialize key/value XML data to C# object - c#

I need deserialize XML file "c:\Temp\Des.xml":
<return xsi:type="ns2:Map"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item>
<key xsi:type="xsd:int">218980</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">218980</value>
</item>
<item>
<key xsi:type="xsd:string">title</key>
<value xsi:type="xsd:string">Product Title 1</value>
</item>
<item>
<key xsi:type="xsd:string">price</key>
<value xsi:type="xsd:string">10.30</value>
</item>
<item>
<key xsi:type="xsd:string">images</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image1.jpg</value>
</item>
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image2.jpg</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:string">specifications</key>
<value SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name1</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value1</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name2</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value2</value>
</item>
</item>
</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:int">218981</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">218981</value>
</item>
<item>
<key xsi:type="xsd:string">title</key>
<value xsi:type="xsd:string">Product Title 2</value>
</item>
<item>
<key xsi:type="xsd:string">price</key>
<value xsi:type="xsd:string">10.40</value>
</item>
<item>
<key xsi:type="xsd:string">images</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image4.jpg</value>
</item>
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image5.jpg</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:string">specifications</key>
<value SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name12</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value12</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name22</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value22</value>
</item>
</item>
</value>
</item>
</value>
</item>
</return>
I created class for that
[Serializable()]
[XmlRoot("Item", Namespace = "", IsNullable = false)]
public class Item
{
[XmlElement("id")]
public int Id { get; set; }
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("price")]
public decimal Price { get; set; }
[XmlElement("images")]
public string[] Images { get; set; }
[XmlElement("specifications")]
public object Specifications { get; set; }
}
and call Deserialize:
public class Program
{
static void Main(string[] args)
{
string xml = File.ReadAllText("c:\\Temp\\Des.xml");
StringReader stringReader = new StringReader(xml);
XmlSerializer serializer = new XmlSerializer(typeof(List<Item>), new XmlRootAttribute("return"));
List<Item> items = (List<Item>) serializer.Deserialize(stringReader);
but items are always empty list, what I'm doing wrong? Thank you!

As #jle said here:
I don't think attributes will work because of the key/value structure.
There is no way for a program to infer from the XML alone what
properties an object has. I would make a static extension method
helper function to get the values:
you can use the below extension method for getting the value of each key:
public static class XmlHelper
{
public static string GetValueByKeyName(this XElement element, string key)
{
return element.Descendants("key")
.First(v => v.Value == key)
.ElementsAfterSelf("value")
.First()
.Value;
}
}
and then, your code became like that:
var ItemObjects = new List<Item>();
XDocument doc = XDocument.Load(#"D:/file.xml");
var elements = doc.Root.Elements("item").ToList();
elements.ForEach(item => {
ItemObjects.Add(new Item {
Id = int.Parse(item.GetValueByKeyName("id")),
Title = item.GetValueByKeyName("title"),
Price = decimal.Parse(item.GetValueByKeyName("price"), CultureInfo.InvariantCulture),
//other props
});
});
I test it with your data, and it works properly. good luck.

Ok, I found solution, in case anyone need it, I use Linq to XML for that:
var items = (from element in xdoc.Descendants("return").Elements("item")
select new
{
Id = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "id").Elements("value").First().Value,
Title = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "title").Elements("value").First().Value,
Price = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "price").Elements("value").First().Value,
Images = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "images").Elements("value").Elements("item").Elements("value").Select(i => i.Value).ToList(),
Specifications = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "specifications").Elements("value").Elements("item").Select(s =>
new KeyValuePair<string, string>
(
s.Elements("item").First().Elements("value").First().Value,
s.Elements("item").First().Elements("value").Last().Value
)
).ToList(),
}
).ToList();

Related

Parsing Soap Message

I recently started to work with SOAP.
Right now I am trying to parse SOAP message in C#.
Message is as it follows:
<SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<ns1:getBuildingsResponse xmlns:ns1="http://someserver.net/~username/lab/servis?ws=1">
<return SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">1</value>
</item>
<item>
<key xsi:type="xsd:string">code</key>
<value xsi:type="xsd:string">345-GESG</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">Building 1</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">7590913</value>
</item>
<item>
<key xsi:type="xsd:string">code</key>
<value xsi:type="xsd:string">353-gr</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">Building 2</value>
</item>
</item>
</return>
I want to extract values of keys id,code and name.
I tried doing something like this:
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(client.Invoke("getBuildings").ToString());
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("i", "item");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("item", nsmgr);
Console.WriteLine(xNodelst.Count);
foreach (XmlNode xn in xNodelst)
{
Console.WriteLine(xn.Value);
}
The problem is, I don't know how to act with tags that have no namespace...
This line of code:
Console.WriteLine(xNodelst.Count);
always prints 0, but I want it to print 2, since I have 2 elements in array (ns2:Map[2]).
Meaning, I want to loop through all of these elements:
<item xsi:type="ns2:Map">
Any help will be appreciated.
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(client.Invoke("getBuildings").ToString());
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
XmlNodeList nodes = xDoc.SelectNodes("//item[#xsi:type='ns2:Map']",nsmgr);
var nodeCount=nodes.Count;
try this, this might help you.

How find element in xdocument and read next elements after them

So i have next xml document:
<Items>
<Item>
<ID>123</ID>
<Name>Super Item</Name>
<Count>1</Count>
<Price>45</Price>
</Item>
<Item>
<ID>456</ID>
<Name>not super Item</Name>
<Count>10</Count>
<Price>5</Price>
</Item>
<Item>
<ID>789</ID>
<Name>Simple Item</Name>
<Count>6</Count>
<Price>10</Price>
</Item>
</Items>
So how can i find needed item by ID and read next values? Thanks in advance.
code:
XDocument doc = XDocument.Load (filePath);
foreach (var item in doc.Descendants("ID"))
{
if ((string)item.Element("ID") == "789")
{
How to read Name "Simple Item"?
How to read Count "6"?
How to read Price "10"?
}
}
By what you are asking you could formatt your xml like this:
<Items>
<Item id="123">
<Name>Super Item</Name>
<Count>1</Count>
<Price>45</Price>
</Item>
<Item id="456">
<Name>not super Item</Name>
<Count>10</Count>
<Price>5</Price>
</Item>
<Item id="789">
<Name>Simple Item</Name>
<Count>6</Count>
<Price>10</Price>
</Item>
</Items>
Then in code:
int yourId = 456;
XDocument doc = XDocument.Load("test.xml");
var result = from el in doc.Root.Elements("Item")
where el.Attribute("id").Value == yourId.ToString()
select el;
The id here is an attribute.And for reading its values 2 ways:
//1º
foreach (var item in result.Elements())
{
Console.WriteLine(item.Name + " = " + item.Value);
}
//2º - will print the element
Console.WriteLine(result);
It depends on what you want to do when you find those values. Here is a general method using a foreach loop to find the item with the specified ID and returning it's name:
private string GetItemName(string _id)
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("myXmlFile.xml");
foreach (XmlNode item in xDoc.SelectNodes("/Items/Item"))
{
if (item.SelectSingleNode("ID").InnerText == _id)
{
// we found the item! Now what do we do?
return item.SelectSingleNode("Name").InnerText;
}
}
return null;
}

Save back to XML file with changed element

I have my XML as:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>`
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std_Ctrl.flg</VALUE>
<VALUE STEP="2">Sas_Card.flg</VALUE>
<VALUE STEP="-1">eSAS1.flg</VALUE>
</INDEX>
<INDEX>
<VALUE>0</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Got the Values using LINQ and displayed all elements in Textbox, only VALUE elements.
var hdd= from n1 in x.Elements("DECLARATION") where 1.Attribute("NAME").Value.Trim() == "1ST_HDD_SATA_Position" select n1.Elements("INSTANCE").Elements("PROPERTY").Elements("INDEX").Elements("VALUE");
Then Updated the textbox to include new values for VALUE Element. For example lets say I updated it to some string.
foreach (IEnumerable<XElement> elList in hdd)
foreach (XElement el in elList)
{
el.ReplaceNodes("string");
el.Save(m);
}
Isn't this suppose to save my xml file with the update?
There are many problems with your code, starting with the fact that you posted invalid XML. Assuming that your XML was like this:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Then the following code works:
var doc = XDocument.Load("Configuration.xml");
var hdd = from n1 in doc.Elements("CONFIGURATION").Elements("DECLARATION")
let nameAttribute = n1.Attribute("NAME")
where nameAttribute != null && nameAttribute.Value.Trim() == "1ST_HDD_SATA"
select n1.Elements("INSTANCE")
.Elements("PROPERTY")
.Elements("INDEX")
.Elements("VALUE");
foreach (var elList in hdd)
foreach (var el in elList)
{
el.ReplaceNodes("string");
}
doc.Save("Configuration.xml");

Deserialization soap response C#

I get a soap response from the service:
<SOAP-ENV:Body>
<ns1:getCurrencyListResponse>
<getCurrencyListReturn SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">18</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">USD</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">19</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">EUR</value>
</item>
</item>
</getCurrencyListReturn>
</ns1:getCurrencyListResponse>
</SOAP-ENV:Body>
File wsdl does not specify a return type. How can I deserializing this messages in C#? The number of "item" and "Map" is not know. Should be similar to this example:
[SoapType(Namespace = "http://xml.apache.org/xml-soap", TypeName = "Map")]
public class Map
{
public item[] item;
}
public class item
{
[SoapElement]
public string key;
[SoapElement]
public string value;
}
In your project you should add a reference to webservice.
In this way a wrapper for webservice is automatically created, letting you call its methods and getting responses using classes inside wrapper.
Serialization and deserialization are transparent to you.

Linq to Xml query to child nodes

<InventoryList>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<Name>Pizza Ristorante Hawaii</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 8,42 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>in</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
<Item>
<ListName>out</ListName>
<Quantity>2</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>2</Id>
<Name>Produto 2</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 5,55 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>out</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
</InventoryList>
thanks in advance for your help.
I have this xml database
i want to return all products that have the ListName = "out", but this query i´m trying it´s only returning the second product, and i need it to return the first and the second product.
var _queryItems = from c in xml.Descendants("Product")
where
c.Element("ItemLists").Element("Item").Element("ListName").Value == "out"
select c;
thanks :)
Right now you just check the first Item element, instead you want to check if any Item's ListName matches "out":
var _queryItems = from c in xml.Descendants("Product")
where c.Element("ItemLists")
.Elements("Item").Any( x=> x.Element("ListName").Value == "out")
select c;

Categories

Resources