C# 将对象序列化为包含对象列表的 XML

C# Serializing an object into XML that includes a list of objects

提问人:tbproto 提问时间:2/23/2023 最后编辑:tbproto 更新时间:2/28/2023 访问量:176

问:

我需要序列化一个 XML 以附加 API PUT 请求。

我正在使用 System.Xml.Serialization。

最终结果需要类似于以下内容:

<?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>

为此,我有这样的类:

[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;
    ...

PaLanguage 是:

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;
}

因为这是更新记录,所以我首先完成一个 get 请求,然后匹配其所有值。 我使用以下代码完成此操作:

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();  

我创建了一个新的 Product 项以附加到我的 put 请求,但在序列化其 XmlArrays 时遇到问题。

我尝试的最后一件事是以下内容:

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") });
}

这是完全错误的,因为它将其序列化为:

<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>

感觉就像我已经尝试了人们可以在网上和官方文档中找到的所有东西,但每次尝试都离我的预期结果更远。

我应该注意,该类实际上还有几十个字段,它们要么只是一个字符串,要么是 id 和语言值的另一种组合。

任何和所有的帮助都是值得赞赏的。

C# LINQ LINQ-to-XML XML 序列化

评论

0赞 sayah imad 2/23/2023
嗨,你的XML中有一些奇怪的东西,它应该是一个产品列表,相反,我看到序列化的结果是产品。
0赞 tbproto 2/23/2023
您好,很抱歉造成混乱。这是因为我展示的示例是一个 GET 响应,它返回产品列表,但我按 ID 对其进行筛选,它只返回 1。但是,PUT 请求将包含产品而不是产品列表。
0赞 sayah imad 2/23/2023
请添加有关错误xml的更多详细信息,这可能会有所帮助
0赞 tbproto 2/23/2023
我已经编辑了 xml,使其类似于成功的 PUT 请求的 xml 数据的样子

答:

0赞 Sebastian 2/23/2023 #1

似乎 XML 中的 delivery_in_stock 和 delivery_out_stock 元素可以包含具有不同 id 属性的多个语言元素。因此,您需要修改 foreach 循环以处理多个语言元素。

您可以使用 Descendants(“language”) 获取delivery_in_stock和delivery_out_stock下的所有语言元素,而不是使用 Elements(“language”)。

foreach (XElement x in gotProduct.Element("delivery_in_stock").Descendants("language"))
{
    product.delivery_in_stock.Add(new PaLanguage { id = (string)x.Attribute("id"), language = (string)x });
}

foreach (XElement x in gotProduct.Element("delivery_out_stock").Descendants("language"))
{
    product.delivery_out_stock.Add(new PaLanguage { id = (string)x.Attribute("id"), language = (string)x });
}

此外,在 PaLanguage 类中,language 属性的类型应为 XmlCDataSection,而不是字符串,因为它包含 CDATA。

public class PaLanguage
{
    [XmlText]
    public XmlCDataSection language { get; set; }

    [XmlAttribute("id")]
    public string id { get; set; }
}

最后,对于产品类,应将 [XmlElement] 属性添加到没有它们的属性中,例如 id_shop_default、reference、supplier_reference 等。

[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; } }

    [XmlElement("cache_default_attribute")]
    public XmlCDataSection C_cache_default_attribute { get { return new XmlDocument().CreateCDataSection(cache_default_attribute); } set { cache_default_attribute = value.Value; } }

    [XmlElement("id_shop_default")]
    public XmlCDataSection C_id_shop_default { get { return new XmlDocument().CreateCDataSection(id_shop_default); } set { id_shop_default = value.Value; } }

    [XmlElement("reference")]
    public XmlCDataSection C_reference { get { return new XmlDocument().CreateCDataSection(reference); } set { reference = value.Value; } }

    [XmlElement("supplier_reference")]
    public XmlCDataSection C_supplier_reference { get { return new XmlDocument().CreateCDataSection(supplier_reference); } set { supplier_reference = value.Value; } }

    [XmlElement("location")]
    public XmlCDataSection C_location { get { return new XmlDocument().CreateCDataSection(location); } set { location = value.Value; } }

    [XmlElement("width")]
    public XmlCDataSection C_width { get { return new XmlDocument().CreateCDataSection(width); } set { width = value.Value; } }

    [XmlElement("height")]
    public XmlCDataSection C_height { get { return new XmlDocument().CreateCDataSection(height); } set { height = value.Value; } }

// etc..

评论

0赞 tbproto 2/28/2023
CS0029:无法隐式将类型“string”转换为“System.Xml.XmlCDataSection”
0赞 sayah imad 2/23/2023 #2

您可以做的是将您当前的 PaLanguage 替换为下面的 PaLanguage,并尝试在发现问题的所有字段上执行相同的操作:


[XmlElement(ElementName = "language")]
public class PaLanguage
{
    [XmlText] 
    public string value { get; set; }
    [XmlAttribute("id")] 
    public string id;
}

评论

0赞 tbproto 2/28/2023
CS0592:属性“XmlElement”对此声明类型无效。它仅对“属性、索引器、字段、参数、返回”声明有效。
0赞 tbproto 2/28/2023 #3

我走在正确的轨道上

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;
}

首先,我将 C_language 更改为 XmlNode[],其 getter 和 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;
}

然后我还更改了“产品”中的列表

[XmlArray("delivery_in_stock")] [XmlArrayItem("language")] public List<PaLanguage> delivery_in_stock;
[XmlArray("delivery_out_stock")] [XmlArrayItem("language")] public List<PaLanguage> delivery_out_stock

最后,正如 Sebastian 建议的那样,我使用了 Descendants 而不是 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") });
                        }

感谢所有帮助过的人。