2

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!

Hamed Moghadasi
  • 1,523
  • 2
  • 16
  • 34
ihorko
  • 6,855
  • 25
  • 77
  • 116
  • 1
    You have an issue with your serializer that is putting in ns2 as a default namespace. You need to fix the serialize method to get rid of the ns2. So to remove the ns2 you need to add to the serializer above the elements creating the ns2 : [XElement(Namespace = "")] – jdweng Sep 26 '20 at 12:16
  • 1
    The type attribute is added when you have inherited classes and need to use XmlInclude. See : https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlincludeattribute?view=netcore-3.1 – jdweng Sep 26 '20 at 12:18

2 Answers2

1

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.

Hamed Moghadasi
  • 1,523
  • 2
  • 16
  • 34
0

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();
ihorko
  • 6,855
  • 25
  • 77
  • 116
  • 1
    There is nothing wrong with using xml linq. I use it all the time. But the real root cause of your issue is the code that did the serialization. The net library inserts namespaces like ns1,ns2, ... when no namespaces are specified which then makes it impossible to deserialize. The correct fix in this case is to add the namespaces (or empty string) so the default namespaces are removed from the xml. – jdweng Sep 26 '20 at 13:58