2011-12-13 24 views
5

Estoy tratando de serializar un objeto que tiene un nombre de nodo duplicado usando C#, la razón por la que tengo que hacer esto es porque estoy construyendo una biblioteca que usa un tercero API.Permitir nombre de nodo duplicado en serialización XML usando C#

La solicitud que necesito para construir se ve así.

<DATASET> 
    <SITE_ID>123</SITE_ID> 
    <DATA type=“name”>Secondary List</DATA> 
    <DATA type="extra" id="CLICKTHRU_URL">http://my.domain.com/</DATA> 
    <DATA type="extra" id="REPLY_FORWARD_EMAIL">[email protected]</DATA> 
    <DATA type="extra" id="REPLY_FROM_EMAIL">[email protected]</DATA> 
    <DATA type="extra" id="REPLY_FROM_NAME">[email protected]</DATA> 
    <DATA type="extra" id="REPLY_FORWARD_SUBJECT">Customer Replies</DATA> 
    <DATA type="extra" id="HANDLE_UNSUBSCRIBE"></DATA> 
    <DATA type="extra" id="HANDLE_AUTOREPLY"></DATA> 
    <DATA type="extra" id="FOOTER_TEXT">Confidentiality agreement…</DATA> 
    <DATA type="extra" id="FOOTER_HTML"> Confidentiality agreement…</DATA> 
</DATASET> 

Mi enfoque es crear una clase que representa la solicitud y el uso de los atributos de serialización XML, la mirada clase como esta:

[XmlRoot("DataSet")] 
public class AddListCallHolder : BaseCallHolder 
{ 
    private BaseAttributeHolder _name = new BaseAttributeHolder(type: ""); 

    [XmlElement("DATA")] 
    public BaseAttributeHolder Name 
    { 
     get { return _name; } 
     set { _name = value; } 
    } 

    private BaseAttributeHolder _clickthruUrl = new BaseAttributeHolder(id: "CLICKTHRU_URL"); 

    [XmlElement("DATA")] 
    public BaseAttributeHolder CLICKTHRU_URL 
    { 
     get { return _clickthruUrl; } 
     set { _clickthruUrl = value; } 
    } 
} 

La clase base del atributo es:

public class BaseAttributeHolder 
{ 
    [XmlAttribute("type")] 
    public string Type { get; set; } 

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

    [XmlText] 
    public string Value { get; set; } 

    public BaseAttributeHolder(string value, string id, string type = "extra") 
    { 
     Type = type; 
     Value = value; 
     Id = id; 
    } 

    public BaseAttributeHolder(string id, string type = "extra") 
    { 
     Type = type; 
     Id = id; 
    } 

    public BaseAttributeHolder(string type = "extra") 
    { 
     Type = type; 
    } 

    public BaseAttributeHolder() 
    { 

    } 
} 

Cuando trato de serializar y me opongo obteniendo este error:

The XML element 'DATA' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.

¿Hay algún trabajo para serializar este objeto u obtener la estructura de la solicitud?

+0

+1 para una primera pregunta se le preguntó muy bien –

Respuesta

2

¿Qué hay de usar una matriz o lista para deserializar todas las <DATA> y luego agregar las propiedades que operan en esta matriz?

[XmlRoot("DataSet")] 
    public class AddListCallHolder 
    { 
     [XmlArrayItem(typeof(BaseAttributeHolder), ElementName = "DATA")] 
     public BaseAttributeHolder[] data 
     { 
      get; 
      set; 
     } 

     [XmlIgnore] 
     public BaseAttributeHolder Name 
     { 
      get 
      { 
      return data.FirstOrDefault(d => d.Type == "name"); 
      } 
     } 

     [XmlIgnore] 
     public BaseAttributeHolder CLICKTHRU_URL 
     { 
      get 
      { 
      return data.FirstOrDefault(d => d.Type == "extra" && d.Id == "CLICKTHRU_URL"); 
      } 
     } 
    } 

Creo que también será capaz de llegar a los setters.

+0

1 para mantener las propiedades. – dash

+0

Gracias, solución limpia. Gracias a todos por la respuesta rápida y la explicación. – dnlgmzddr

0

No puede hacer esto sin personalizar la serialización usted mismo; el problema es que el serializador necesita identificar de manera única las propiedades serializadas en la salida. Si todos tienen el nombre "DATOS", realmente no puede hacer esto.

Lo que podría hacer en su lugar es:

[XmlType(TypeName="Data")] 
public class BaseAttributeHolder 
{ 
    [XmlAttribute("type")] 
    public string Type { get; set; } 

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

    [XmlText] 
    public string Value { get; set; } 


    public BaseAttributeHolder(string value, string id, string type = "extra") 
    { 
     Type = type; 
     Value = value; 
     Id = id; 
    } 

    public BaseAttributeHolder(string id, string type = "extra") 
    { 
     Type = type; 
     Id = id; 
    } 

    public BaseAttributeHolder(string type = "extra") 
    { 
     Type = type; 
    } 

    public BaseAttributeHolder() 
    { 

    } 
} 

[XmlRoot("DataSet")] 
public class AddListCallHolder 
{ 
    [XmlElement("SITE_ID")] 
    public string Site =""; 

    private BaseAttributeHolder _name = new BaseAttributeHolder(type: ""); 

    [XmlElement("DATA")] 
    public List<BaseAttributeHolder> Attributes { get; set; } 


    public AddListCallHolder() 
    { 
     Attributes = new List<BaseAttributeHolder>(); 
     Attributes.Add(new BaseAttributeHolder(id: "CLICKTHRU_URL")); 
     Attributes.Add(new BaseAttributeHolder()); 

    } 


} 

Por lo que su clase AddCallHolder expondrá sus atributos como una lista de atributos.

Si a continuación serializar lo anterior:

AddListCallHolder callHolder = new AddListCallHolder(); 

    XmlSerializer ser = new XmlSerializer(typeof(AddListCallHolder)); 

    StringBuilder sb = new StringBuilder(); 

    TextWriter writer = new StringWriter(sb); 

    ser.Serialize(writer, callHolder); 

    sb.ToString(); 

se obtendrá:

<?xml version="1.0" encoding="utf-16"?> 
    <DataSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
      <SITE_ID /> 
      <Data type="extra" id="CLICKTHRU_URL" /> 
      <Data /> 
    </DataSet> 

¿Cuál es la única manera de hacerlo (sin necesidad de escribir sus propios métodos de serialización que está bien pero obviamente más trabajo).

EDIT:

hecho, me gusta la solución de Krizz mientras mantiene las propiedades originales en su clase, utilizando la matriz como un contenedor para la serialización y la XmlIgnore atributo de tal manera que el serializador hace caso omiso de las propiedades de serialización/deserialización. Las propiedades solo se convierten en búsquedas/inserciones de matriz en la matriz.

0
[XmlRoot("DATASET")] 
public class DS 
{ 
    [XmlElement("SITE_ID")] 
    public string Site =""; 

    [XmlElement("DATA")] 
    public Data[] Data = null; 

} 

[XmlRoot("DATA")] 
public class Data 
{ 
    [XmlAttribute("type")] 
    public string Type =""; 

    [XmlAttribute("id")] 
    public string Id = null; 

    [XmlText] 
    public string Text = ""; 
} 

XmlSerializer xs = new XmlSerializer(typeof(DS)); 
var obj = xs.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(xmlstring))); 

MemoryStream m = new MemoryStream(); 
xs.Serialize(m,obj); 
MessageBox.Show(Encoding.UTF8.GetString(m.ToArray())); 

y aquí es su xmlString

string xmlstring = 
    @" 
<DATASET> 
    <SITE_ID>123</SITE_ID> 
    <DATA type=""name"">Secondary List</DATA> 
    <DATA type=""extra"" id=""CLICKTHRU_URL"">http://my.domain.com/</DATA> 
    <DATA type=""extra"" id=""REPLY_FORWARD_EMAIL"">[email protected]</DATA> 
    <DATA type=""extra"" id=""REPLY_FROM_EMAIL"">[email protected]</DATA> 
    <DATA type=""extra"" id=""REPLY_FROM_NAME"">[email protected]</DATA> 
    <DATA type=""extra"" id=""REPLY_FORWARD_SUBJECT"">Customer Replies</DATA> 
    <DATA type=""extra"" id=""HANDLE_UNSUBSCRIBE""></DATA> 
    <DATA type=""extra"" id=""HANDLE_AUTOREPLY""></DATA> 
    <DATA type=""extra"" id=""FOOTER_TEXT"">Confidentiality agreement…</DATA> 
    <DATA type=""extra"" id=""FOOTER_HTML""> Confidentiality agreement…</DATA> 
</DATASET> 
";