2011-07-07 91 views
6

Estoy analizando algunos archivos XML de un proveedor de terceros y, lamentablemente, no siempre está bien formado XML, ya que a veces algunos elementos contienen atributos duplicados.Cómo eliminar atributos duplicados de XML con C#

No tengo control sobre la fuente y no sé qué elementos pueden tener atributos duplicados ni conozco los nombres de los atributos duplicados de antemano.

Obviamente, la carga del contenido en un objeto XMLDocument plantea una XmlException en el duplicado atributos así que aunque podría utilizar un XmlReader al paso de que el elemento XML a elemento y hacer frente a los atributos de duplicado cuando llegue al elemento infractor.

Sin embargo, el XmlException se genera en reader.Read() - antes de que tenga la oportunidad de insertar los atributos del elemento.

Aquí está un método de ejemplo para demostrar el problema:

public static void ParseTest() 
{ 
    const string xmlString = 
     @"<?xml version='1.0'?> 
     <!-- This is a sample XML document --> 
     <Items dupattr=""10"" id=""20"" dupattr=""33""> 
      <Item>test with a child element <more/> stuff</Item> 
     </Items>"; 

    var output = new StringBuilder(); 
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString))) 
    { 
     XmlWriterSettings ws = new XmlWriterSettings(); 
     ws.Indent = true; 
     using (XmlWriter writer = XmlWriter.Create(output, ws)) 
     { 
      while (reader.Read()) /* Exception throw here when Items element encountered */ 
      { 
       switch (reader.NodeType) 
       { 
        case XmlNodeType.Element: 
         writer.WriteStartElement(reader.Name); 
         if (reader.HasAttributes){ /* CopyNonDuplicateAttributes(); */} 
         break; 
        case XmlNodeType.Text: 
         writer.WriteString(reader.Value); 
         break; 
        case XmlNodeType.XmlDeclaration: 
        case XmlNodeType.ProcessingInstruction: 
         writer.WriteProcessingInstruction(reader.Name, reader.Value); 
         break; 
        case XmlNodeType.Comment: 
         writer.WriteComment(reader.Value); 
         break; 
        case XmlNodeType.EndElement: 
         writer.WriteFullEndElement(); 
         break; 
       } 
      } 

     } 
    } 
    string str = output.ToString(); 
} 

¿Hay otra manera de analizar la entrada y quitar los atributos de duplicado sin tener que usar expresiones regulares y la manipulación de cadenas?

+0

Solo puede ser posible si el proveedor de la API del procesador XML tiene ganchos que le permiten enganchar en el procesamiento y manejar las condiciones de error – Ankur

+0

¡Problema interesante, esperamos ver la solución! –

+2

No habrá solución a este problema utilizando XML, porque su entrada no es XML. Usted dice que no tiene control sobre la entrada, pero ¿al menos puede hacer que sus superiores sepan que su proveedor no le envía XML? ¿Al menos puedes asegurarte de que tu _vendor_ lo sepa?Cualquier organización lo suficientemente estúpida como para enviar estos datos podría ser lo suficientemente estúpida como para no darse cuenta de que no es XML. –

Respuesta

3

Encontré una solución pensando en el XML como un documento HTML. Luego, utilizando la biblioteca de código abierto Html Agility Pack, pude obtener XML válido.

El truco era guardar primero el xml con un encabezado HTML.
Así sustituir a la declaración XML
<?xml version="1.0" encoding="utf-8" ?>
con una declaración de HTML así:
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Una vez que el contenido se guarda en un archivo, este método devolverá un documento XML válido.

// Requires reference to HtmlAgilityPack 
public XmlDocument LoadHtmlAsXml(string url) 
{ 
    var web = new HtmlWeb(); 

    var m = new MemoryStream(); 
    var xtw = new XmlTextWriter(m, null); 

    // Load the content into the writer 
    web.LoadHtmlAsXml(url, xtw); 

    // Rewind the memory stream 
    m.Position = 0; 

    // Create, fill, and return the xml document 
    XmlDocument xmlDoc = new XmlDocument(); 
    xmlDoc.LoadXml((new StreamReader(m)).ReadToEnd()); 
    return xmlDoc; 
} 

Los nodos de atributos duplicados son retira automáticamente con los valores de atributo posteriores sobrescribiendo los anteriores.

0

Ok pienso necesidad de detectar el error:

, entonces debería ser capaz de utilizar los métodos siguientes:

reader.MoveToFirstAttribute(); 

y

reader.MoveToNextAttribute() 

para obtener las siguientes propiedades:

reader.Value 
reader.Name 

Esto le permitirá obtener todos los valores de los atributos.

+0

Puedo detectar el error y procesar los atributos en el nodo actual (es decir, copiar no duplicados), pero el problema continúa con el procesamiento del resto del documento, ya que 'reader.Read()' devuelve falso por lo que no se procesan más elementos. – Catch22

+0

# Catch22, sí, me encontré con eso mientras trataba de obtener el código para reanudar. Esperaba que encontraras una forma de evitarlo. Eche un vistazo aquí: http://bytes.com/topic/c-sharp/answers/827965-how-handle-xml-parsing-exception parece que XMLReader es un error intolerante por alguna razón. Normalmente, esto sería una buena noticia, pero en su caso significa que probablemente mi solución sugerida no funcionará. lo siento – openshac

Cuestiones relacionadas