leyendo CDATA con linq a xml
-
03-07-2019 - |
Pregunta
Tengo el siguiente archivo xml y parece que no puedo entender cómo obtener el valor en los elementos (que están enterrados en un CDATA). Estoy tratando de usar linq to xml para esto. Si alguien sabe cómo convertiría esto en un "Producto" objeto (supongamos que tenemos un objeto producto que tiene propiedades con los mismos nombres que los elementos). Gracias de antemano.
bob
<CNETResponse realm="cnet" version="1.0" xmlns="http://api.cnet.com/rest/v1.0/ns" xmlns:xlink="http://www.w3.org/1999/xlink">
<TechProduct id="33517677">
<Name><![CDATA[Nikon CoolPix L20 (deep red)]]></Name>
<Topic id="1670"></Topic>
<ImageURL width="60"><![CDATA[http://i.i.com.com/cnwk.1d/sc/33517677-2-60-0.gif]]></ImageURL>
</TechProduct>
</CNETResponse>
Solución
El problema son los espacios de nombres, por ejemplo, algo así como:
XNamespace ns = "http://api.cnet.com/rest/v1.0/ns";
XElement techProd = doc.Root.Element(ns + "TechProduct");
Product product = new Product {
Id = (int)techProd.Attribute("id"),
Name = techProd.Element(ns + "Name").Value,
Topic = techProd.Element(ns + "Topic").Value,
TopicId = (int)techProd.Element(ns + "Topic").Attribute("id"),
ImageUrl = techProd.Element(ns + "ImageURL").Value,
ImageWidth = (int)techProd.Element(ns + "ImageURL").Attribute("width"),
};
También puede preferir XmlSerializer
, algo así como:
XmlSerializer ser = new XmlSerializer(typeof(CnetResponse));
CnetResponse response = (CnetResponse)ser.Deserialize(new StringReader(xml));
TechProduct product = response.TechProduct;
Con definiciones de clase como:
[Serializable, XmlRoot("CNETResponse", Namespace = CnetResponse.Namespace)]
public class CnetResponse {
public const string Namespace = "http://api.cnet.com/rest/v1.0/ns";
public TechProduct TechProduct { get; set; }
}
[Serializable, XmlType(Namespace = CnetResponse.Namespace)]
public class TechProduct
{
[XmlAttribute("id")]
public int Id { get; set; }
public string Name {get;set;}
public Topic Topic { get; set; }
[XmlElement("ImageURL")]
public Image Image { get; set; }
}
[Serializable, XmlType(Namespace = CnetResponse.Namespace)]
public class Topic {
[XmlAttribute("id")]
public int Id { get; set; }
[XmlText]
public string Text {get;set;}
}
[Serializable, XmlType(Namespace = CnetResponse.Namespace)]
public class Image {
[XmlAttribute("width")]
public int Width { get; set; }
[XmlText]
public string Url {get;set;}
}
O, como alternativa, simplemente ejecute el xml a través de xsd.exe
para obtener el código C # adecuado:
xsd foo.xml
xsd foo.xsd /classes
Otros consejos
Suponiendo que su clase Product
tiene el constructor utilizado aquí, intente esto, donde rawXml
es el XML de respuesta CNET:
XElement cnetResponse = XElement.Parse(rawXml);
IEnumerable<NameQty> products =
from e in cnetResponse.Descendants("TechProduct")
select new Product(
(string)e.Element("Name"),
(int)e.Element("Topic").Attribute("id"),
(string)e.Element("ImageURL")
);
foreach(Product p in products)
{
// do stuff
}
No tengo acceso a una máquina en la que probar esto, así que no garantizo.