2010-06-23 16 views
6

Ho il seguente codice di analisi XML nella mia domanda:pause XmlReader in UTF-8 BOM

public static XElement Parse(string xml, string xsdFilename) 
    { 
     var readerSettings = new XmlReaderSettings 
     { 
      ValidationType = ValidationType.Schema, 
      Schemas = new XmlSchemaSet() 
     }; 
     readerSettings.Schemas.Add(null, xsdFilename); 
     readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema; 
     readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation; 
     readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings; 
     readerSettings.ValidationEventHandler += 
      (o, e) => { throw new Exception("The provided XML does not validate against the request's schema."); }; 

     var readerContext = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.UTF8); 

     return XElement.Load(XmlReader.Create(new StringReader(xml), readerSettings, readerContext)); 
    } 

Io lo utilizzo per analizzare le stringhe inviate al mio servizio WCF in documenti XML, per la deserializzazione personalizzato.

Funziona bene quando leggo i file e li mando sul filo (la richiesta); Ho verificato che il BOM non è stato inviato. Nel gestore della richiesta sto serializzando un oggetto risposta e inviandolo indietro come stringa. Il processo di serializzazione aggiunge un BOM UTF-8 alla parte anteriore della stringa, causando la rottura dello stesso codice durante l'analisi della risposta.

Nella ricerca che ho fatto nell'ultima ora circa, sembra che XmlReader debba onorare il BOM. Se rimuovo manualmente il BOM dalla parte anteriore della stringa, la risposta xml è corretta.

Mi manca qualcosa di ovvio, o almeno qualcosa di insidioso?

EDIT: Ecco il codice di serializzazione che sto usando per restituire la risposta:

private static string SerializeResponse(Response response) 
{ 
    var output = new MemoryStream(); 
    var writer = XmlWriter.Create(output); 
    new XmlSerializer(typeof(Response)).Serialize(writer, response); 
    var bytes = output.ToArray(); 
    var responseXml = Encoding.UTF8.GetString(bytes); 
    return responseXml; 
} 

Se è solo una questione di XML che contiene in modo errato il BOM, allora io passare a

var responseXml = new UTF8Encoding(false).GetString(bytes); 

ma dalla mia ricerca non risultava chiaramente che il DB era illegale nella stringa XML effettiva; vedere per es. c# Detect xml encoding from Byte Array?

+0

Ho avuto questo problema qui: http://stackoverflow.com/questions/291455/xml-data-at-root-level-is-invalid –

risposta

5

La stringa xml non deve (!) Contenere la distinta materiali, la distinta materiali è consentita solo nei dati di byte (ad esempio flussi) codificati con UTF-8. Questo perché la rappresentazione della stringa non è codificata, ma già una sequenza di caratteri Unicode.

Sembra quindi che tu abbia caricato la stringa errata, che è in codice che purtroppo non hai fornito.

Edit:

Grazie per aver postato il codice di serializzazione.

Non è necessario scrivere i dati su un MemoryStream, ma piuttosto su un StringWriter che è possibile convertire in una stringa con ToString. Poiché evita di passare attraverso una rappresentazione di byte, non solo è più veloce, ma evita anche questi problemi.

Qualcosa di simile a questo:

private static string SerializeResponse(Response response) 
{ 
    var output = new StringWriter(); 
    var writer = XmlWriter.Create(output); 
    new XmlSerializer(typeof(Response)).Serialize(writer, response); 
    return output.ToString(); 
} 
+0

Ho fatto esattamente quel cambiamento, e funziona perfettamente. Grazie! – arootbeer

+0

Non esiste alcuna limitazione per la distinta materiali presente in XML in base a questo: http://www.w3.org/TR/REC-xml/#charencoding – knocte

+0

Questo funziona ... tuttavia, quando si passa a un ' StringWriter', l'attributo 'encoding' nella' 'dichiarazione sembra sempre apparire come UTF-16. Per, per esempio, UTF-8, devi 'return output.ToString(). Replace (" utf-16 "," utf-8 ");'. – David

0

La distinta base non dovrebbe essere nella stringa, in primo luogo.
Le BOM vengono utilizzate per rilevare la codifica di una matrice di byte non elaborata; non hanno affari essendo in una stringa reale.

Da cosa viene la stringa?
Probabilmente lo stai leggendo con la codifica sbagliata.

+0

Mi sono assicurato che stavo usando almeno la codifica giusta :) Ho aggiunto il codice di serializzazione alla mia domanda. – arootbeer

+0

Questa è una risposta interessante ... Ho un caso in cui sto tirando da un'API remota (che non controllo) e sto semplicemente caricando i dati tramite req.GetResponse(). GetResponseStream() e mettendo che scorre direttamente in XmlReader. C'è un modo migliore per farlo (che evita questo problema)? –

+0

@TomLianza: Dipende. Che byte sta inviando? – SLaks

0

Le stringhe in C# sono codificate come UTF-16, quindi il BOM sarebbe sbagliato. Come regola generale, codificare sempre XML in matrici di byte e decodificarlo da matrici di byte.

+0

Questo non è esattamente vero. Mentre il formato di memoria è in genere simile a UTF-16, le stringhe sono una sequenza "astratta" di caratteri con un numero specifico di caratteri. Si noti che ci sono state discussioni nel team CLR per cambiare le stringhe per avere un'altra rappresentazione in memoria al fine di renderle più efficienti. Ad ogni modo, poiché è una vista astratta e non una sequenza di byte, una BOM non deve esistere nella stringa. – Lucero

+0

Ho aggiunto il codice di serializzazione. Sto già usando UTF-8 esplicitamente. – arootbeer

+0

@Stephen, penso che la cosa con rappresentazioni alternative di stringa in memoria sia nel seguente video di Channel 9: http://channel9.msdn.com/shows/Going+Deep/Vance-Morrison-CLR-Through-the-Years/ – Lucero

9

Nel gestore della richiesta sto serializzando un oggetto risposta e inviandolo indietro come stringa. Il processo di serializzazione aggiunge un BOM UTF-8 alla parte anteriore della stringa, causando la rottura dello stesso codice durante l'analisi della risposta.

Pertanto, si desidera impedire l'aggiunta della distinta componenti come parte del processo di serializzazione. Sfortunatamente, non fornisci la logica di serializzazione.

Che cosa si dovrebbe fare è fornire un esempio UTF8Encoding creato tramite il costruttore UTF8Encoding(bool) per disabilitare la generazione della distinta base, e passare questa Encoding istanza a qualsiasi metodo che si sta utilizzando, che sta generando la stringa intermedio.

+0

Grazie! Mi sono imbattuto in quel po 'di saggezza durante la mia ricerca, ma non sono riuscito a trovare alcuna indicazione esplicita per includere o escludere il BOM. – arootbeer

+0

Oggi mi hai aiutato molto, ottima soluzione! –

+0

Questo è tutto! Grazie! – Grubl3r