I need to serialize an XML to attach for an API PUT request.
I am using System.Xml.Serialization.
The end result needs to resemble the following:
<?xml version="1.0" encoding="UTF-8"?>
<prestashop xmlns:xlink="http://www.w3.org/1999/xlink">
<product>
<id>
<![CDATA[2678]]>
</id>
<cache_default_attribute>
<![CDATA[0]]>
</cache_default_attribute>
<id_shop_default>
<![CDATA[1]]>
</id_shop_default>
<reference>
<![CDATA[2678]]>
</reference>
<supplier_reference>
<![CDATA[]]>
</supplier_reference>
<location>
<![CDATA[]]>
</location>
<width>
<![CDATA[0.000000]]>
</width>
<height>
<![CDATA[0.000000]]>
</height>
<depth>
<![CDATA[0.000000]]>
</depth>
<weight>
<![CDATA[0.000000]]>
</weight>
<quantity_discount>
<![CDATA[0]]>
</quantity_discount>
<ean13>
<![CDATA[]]>
</ean13>
<isbn>
<![CDATA[]]>
</isbn>
<upc>
<![CDATA[0]]>
</upc>
<mpn>
<![CDATA[000.018]]>
</mpn>
<cache_is_pack>
<![CDATA[0]]>
</cache_is_pack>
<cache_has_attachments>
<![CDATA[0]]>
</cache_has_attachments>
<is_virtual>
<![CDATA[0]]>
</is_virtual>
<state>
<![CDATA[1]]>
</state>
<additional_delivery_times>
<![CDATA[1]]>
</additional_delivery_times>
<delivery_in_stock>
<language id="1">
<![CDATA[]]>
</language>
<language id="2">
<![CDATA[]]>
</language>
</delivery_in_stock>
<delivery_out_stock>
<language id="1">
<![CDATA[]]>
</language>
<language id="2">
<![CDATA[]]>
</language>
</delivery_out_stock>
<product_type>
<![CDATA[combinations]]>
</product_type>
<on_sale>
<![CDATA[0]]>
</on_sale>
<online_only>
<![CDATA[0]]>
</online_only>
<date_add>
<![CDATA[2023-02-16 00:21:12]]>
</date_add>
<date_upd>
<![CDATA[2023-02-22 17:22:23]]>
</date_upd>
<name>
<language id="1">
<![CDATA[ETNIES TRI LAM POLO (000.018)]]>
</language>
<language id="2">
<![CDATA[ETNIES TRI LAM POLO (000.018)]]>
</language>
</name>
</product>
</prestashop>
For this purpose I have a class like this:
[XmlType("product")]
public class Product
{
public Product()
{
id = "";
active = "1";
available_for_order= "1";
indexed= "1";
visibility = "both";
delivery_in_stock = new List<PaLanguage>();
delivery_out_stock = new List<PaLanguage>();
name = new List<PaLanguage>();
}
[XmlElement("id")] public XmlCDataSection C_id { get { return new XmlDocument().CreateCDataSection(id); } set { id = value.Value; } }
[XmlIgnore] public string id;
[XmlElement("cache_default_attribute")] public XmlCDataSection C_cache_default_attribute { get { return new XmlDocument().CreateCDataSection(cache_default_attribute); } set {cache_default_attribute = value.Value; } }
[XmlIgnore] public string cache_default_attribute;
...
[XmlArray("delivery_in_stock")] public List<PaLanguage> delivery_in_stock;
[XmlArray("delivery_out_stock")] public List<PaLanguage> delivery_out_stock;
...
With PaLanguage being:
public class PaLanguage
{
[XmlElement("language")] public XmlCDataSection C_language { get { return new XmlDocument().CreateCDataSection(language); } set { language = value.Value; } }
[XmlIgnore] public string language { get; set; }
[XmlAttribute("id")] public string id;
}
Because this is updating a record I first complete a get request and then match all of its values. I accomplish this with the following code:
XDocument getResponse = Helper.GetProductReference(get, Helper.GetValueByKey("ShopApi") + "products/");
var gotProduct = getResponse
.Element("prestashop")
.Element("products")
.Elements("product")
.Where(e => e.Element("reference").Value == mtrl)
.Single();
I create a new Product item to attach to my put request but I have trouble serializing its XmlArrays.
The last thing I tried was the following:
foreach (XElement x in gotProduct.Element("delivery_in_stock").Elements("language"))
{
product.delivery_in_stock.Add(new PrestaLanguage { id = (string)x.Attribute("id"), language = (string)x.Element("language") });
}
Which is completely wrong because it serializes it as:
<product>
<id><![CDATA[2678]]></id>
... xml
<delivery_in_stock>
<PaLanguage id="1">
<language><![CDATA[]]></language>
</PaLanguage>
<PaLanguage id="2">
<language><![CDATA[]]></language>
</PaLanguage>
</delivery_in_stock>
It feels like I've tried everything one can find online and in official documentation and only get further from my intended result with each attempt.
I should note the class actually has dozens more fields and they're all either just a string or another combination of id and a language value.
Any and all help is appreciated.
I was on the right track with
public class PaLanguage
{
[XmlElement("language")] public XmlCDataSection C_language { get { return new XmlDocument().CreateCDataSection(language); } set { language = value.Value; } }
[XmlIgnore] public string language { get; set; }
[XmlAttribute("id")] public string id;
}
First I changed C_language into an XmlNode[] with a slightly different getter and setter
public class PaLanguage
{
[XmlText] public XmlNode[] C_language
{
get { var dummy = new XmlDocument(); return new XmlNode[] { dummy.CreateCDataSection(language) }; }
set { var dummy = (XmlCDataSection)value[0]; language = dummy.Data; }
}
[XmlIgnore] public string language { get; set; }
[XmlAttribute("id")] public string id;
}
Then I also changed the lists in Product
[XmlArray("delivery_in_stock")] [XmlArrayItem("language")] public List<PaLanguage> delivery_in_stock;
[XmlArray("delivery_out_stock")] [XmlArrayItem("language")] public List<PaLanguage> delivery_out_stock
Finally as Sebastian recommended, I used Descendants instead of Elements.
foreach (XElement x in gotProduct.Element("delivery_in_stock").Descendants("language"))
{
product.delivery_in_stock.Add(new PrestaLanguage { id = (string)x.Attribute("id"), language = (string)x.Element("language") });
}
foreach (XElement x in gotProduct.Element("delivery_out_stock").Descendants("language"))
{
product.delivery_out_stock.Add(new PrestaLanguage { id = (string)x.Attribute("id"), language = (string)x.Element("language") });
}
Thanks to everyone that helped.