2010-06-23 5 views
6

Ich habe den folgenden XML-Parsing-Code in meiner Anwendung:XmlReader bricht auf 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)); 
    } 

ich bin mit Strings zu analysieren, um meinen WCF-Dienst in XML-Dokumente gesendet werden, für individuelle Deserialisierung.

Es funktioniert gut, wenn ich Dateien einlese und sie über die Leitung (die Anfrage) sende; Ich habe überprüft, dass die Stückliste nicht gesendet wird. In meinem Request-Handler serialisiere ich ein Response-Objekt und sende es als String zurück. Der Serialisierungsprozess fügt eine UTF-8-Stückliste an der Vorderseite der Zeichenfolge hinzu, wodurch derselbe Code beim Analysieren der Antwort unterbrochen wird.

System.Xml.XmlException : Data at the root level is invalid. Line 1, position 1. 

In der Forschung, die ich in der letzten Stunde oder so getan habe, scheint XmlReader die Stückliste zu beachten. Wenn ich die Stückliste manuell vor der Zeichenfolge lösche, analysiert die Antwort-XML.

Fehle ich etwas offensichtlich, oder zumindest etwas heimtückisch?

EDIT: Hier ist die Serialisierungscode ich die Antwort zurück bin mit:

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

Wenn es nur eine Frage der xml falsch die BOM enthält, dann werde ich zu

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

aber aus meinen Nachforschungen war überhaupt nicht klar, dass die Stückliste in der eigentlichen XML-Zeichenfolge unzulässig war; siehe z.B. c# Detect xml encoding from Byte Array?

+0

Ich hatte dieses Problem hier: http://stackoverflow.com/questions/291455/xml-data-at-root-level-is-invalid –

Antwort

5

Die XML-Zeichenfolge darf nicht (!) Die Stückliste enthalten, die Stückliste ist nur in Bytedaten (z. B. Streams) zulässig, die mit UTF-8 codiert sind. Dies liegt daran, dass die Zeichenfolgendarstellung nicht codiert ist, sondern bereits eine Folge von Unicode-Zeichen.

Es scheint daher, dass Sie die Zeichenfolge falsch laden, die in Code ist, den Sie leider nicht zur Verfügung gestellt haben.

Edit:

Vielen Dank für die Serialisierung Code veröffentlichen.

Sie sollten die Daten nicht in einen MemoryStream schreiben, sondern in einen StringWriter, den Sie dann in einen String mit ToString konvertieren können. Da dies das Durchlaufen einer Byte-Darstellung vermeidet, ist es nicht nur schneller, sondern vermeidet auch solche Probleme.

Etwas wie folgt aus:

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

Ich habe genau diese Änderung gemacht, und es funktioniert perfekt. Vielen Dank! – arootbeer

+0

Es gibt keine Einschränkung für die Stückliste, die in XML entsprechend vorhanden ist: http://www.w3.org/TR/REC-xml/#charencoding – knocte

+0

Das funktioniert aber ... wenn Sie zu einem 'wechseln StringWriter ', scheint das' encoding' Attribut in der '' Deklaration immer als UTF-16 zu erscheinen. Für, sagen wir, UTF-8, müssen Sie 'output.ToString() zurückgeben. Ersetzen (" utf-16 "," utf-8 ");'. – David

0

Die BOM sollte nicht an erster Stelle in der Zeichenfolge sein.
BOMs werden verwendet, um die Codierung eines Raw-Byte-Arrays zu erkennen; Sie haben kein Geschäft in einer tatsächlichen Zeichenfolge.

Woher kommt die Saite?
Sie lesen es wahrscheinlich mit der falschen Kodierung.

+0

Ich stellte sicher, dass ich mindestens die richtige Kodierung verwendete :) Ich habe den Serialisierungscode zu meiner Frage hinzugefügt. – arootbeer

+0

Dies ist eine interessante Antwort ... Ich habe einen Fall, wo ich von einer Remote-API (die ich nicht kontrolliere) ziehe und ich lade einfach die Daten über req.GetResponse(). GetResponseStream() und Putting Dieser Stream direkt in einen XmlReader. Gibt es einen besseren Weg (was dieses Problem vermeidet)? –

+0

@TomLianza: Das hängt davon ab. Welche Bytes sendet es? – SLaks

0

Zeichenfolgen in C# sind als UTF-16 codiert, daher ist die Stückliste falsch. Als allgemeine Regel muss XML immer in Byte-Arrays codiert und aus Byte-Arrays dekodiert werden.

+0

Das stimmt nicht genau. Während das Speicherformat normalerweise UTF-16 ähnelt, sind Zeichenfolgen eine "abstrakte" Zeichenfolge mit einer bestimmten Anzahl von Zeichen. Beachten Sie, dass im CLR-Team Diskussionen geführt wurden, um Zeichenfolgen so zu ändern, dass sie eine andere speicherinterne Repräsentation enthalten, um sie effizienter zu machen. Da es sich um eine abstrakte Sicht und nicht um eine Bytefolge handelt, darf eine Stückliste in der Zeichenfolge nicht vorhanden sein. – Lucero

+0

Ich habe den Serialisierungscode hinzugefügt. Ich verwende bereits UTF-8 explizit. – arootbeer

+0

@Stephen, ich denke, die Sache mit alternativen In-Memory-String-Darstellungen war im folgenden Channel 9-Video: http://channel9.msdn.com/shows/Going+Deep/Vance-Morrison-CLR-Through-the-Years/ – Lucero

9

In meinem Request-Handler serialisiere ich ein Antwortobjekt und sende es als String zurück. Der Serialisierungsprozess fügt eine UTF-8-Stückliste an der Vorderseite der Zeichenfolge hinzu, wodurch derselbe Code beim Analysieren der Antwort unterbrochen wird.

Sie möchten also verhindern, dass die Stückliste als Teil Ihres Serialisierungsprozesses hinzugefügt wird. Leider liefern Sie nicht, was Ihre Serialisierungslogik ist.

Was Sie tun sollen, bietet eine UTF8Encoding Instanz über den UTF8Encoding(bool) Konstruktor erstellt Generation der BOM zu deaktivieren, und übergibt diese Encoding Instanz je nachdem, welche Methode Sie verwenden, die Ihre Zwischenkette generieren.

+0

Danke! Ich bin während meiner Recherche auf diese Weisheit gestoßen, konnte jedoch keine expliziten Anweisungen zum Einschließen oder Ausschließen der Stückliste finden. – arootbeer

+0

Hat mir heute viel geholfen, tolle Lösung! –

+0

Das ist es! Vielen Dank! – Grubl3r