2016-11-12 1 views
0

Ich versuche, einen kleinen Ausschnitt von XML mit dem DataContractSerializer deserialise:DataContractSerializer: SerializationException ‚Endelement‘ ist nicht zu erwarten

<iopbase:SoapFaultException xmlns:iopbase=""http://example.com/foo/""> 
    <faultcode></faultcode> 
    <faultstring>Hello World</faultstring> 
</iopbase:SoapFaultException> 

Das Problem ist die DataContractSerializer nicht ‚sehen‘ die <faultcode> und <faultstring> Elemente und ich erhalte die folgende Ausnahme:

SerializationException: 'EndElement' 'SoapFaultException' from namespace 
'http://example.com/foo/' is not expected. Expecting element 'faultcode'. 

Hier ist eine abgespeckte Stück Code, das Problem zu reproduzieren:

[DataContract(Namespace = "http://example.com/foo/")] 
public class SoapFaultException 
{ 
    [DataMember(IsRequired = true)] 
    public string faultcode { get; set; } 

    [DataMember(IsRequired = true)] 
    public string faultstring { get; set; } 
} 

var xml = System.Xml.Linq.XElement.Parse(@" 
<iopbase:SoapFaultException xmlns:iopbase=""http://example.com/foo/""> 
    <faultcode></faultcode> 
    <faultstring>Hello World</faultstring> 
</iopbase:SoapFaultException>"); 

var serializer = new DataContractSerializer(typeof(SoapFaultException)); 
var e = (SoapFaultException) serializer.ReadObject(xml.CreateReader()); <- Exception Here 

Und schließlich, Stack-Trace der Ausnahme.

at System.Runtime.Serialization.XmlObjectSerializerReadContext.ThrowRequiredMemberMissingException(XmlReaderDelegator xmlReader, Int32 memberIndex, Int32 requiredIndex, XmlDictionaryString[] memberNames) 
    at ReadSoapFaultExceptionFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[]) 
    at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns) 
    at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver) 
    at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver) 
    at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader) 
    at XXX.Program.Main() in C:\Git\XXX\Program.cs:line 161 
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 

Antwort

1

Der Grund, warum Sie die Ausnahme sehen, ist, dass die Namensräume der Elemente <faultcode> und <faultstring> mit dem Datenvertrag unvereinbar sind.

Auf der einen Seite befindet sich das Stammelement in Ihrem XML im Namespace xmlns:iopbase="http://example.com/foo/", aber seine untergeordneten Elemente befinden sich nicht in einem Namespace, dem das Präfix xmlns: fehlt. Auf der anderen Seite geben Sie durch Hinzufügen von [DataContract(Namespace = "http://example.com/foo/")] zu SoapFaultException an, dass das SoapFaultException Stammelement sowie seine untergeordneten Elemente im angegebenen Namespace erscheinen sollen. Und da sich die XML-Elemente nicht in diesem Namespace befinden, werden sie nicht erfolgreich deserialisiert.

Um dies zu bestätigen, wenn ich eine Instanz des Typs serialisiert aus, xmlns:iopbase="http://example.com/foo/" die Namespacepräfix Einstellung, die ich erhalte:

<iopbase:SoapFaultException xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/foo/" xmlns:iopbase="http://example.com/foo/"> 
    <iopbase:faultcode></iopbase:faultcode> 
    <iopbase:faultstring>Hello World</iopbase:faultstring> 
</iopbase:SoapFaultException> 

den Code mit

var test = new SoapFaultException { faultcode = "", faultstring = "Hello World" }; 

var serializer = new DataContractSerializer(typeof(SoapFaultException)); 
var doc = new XDocument(); 
using (var writer = doc.CreateWriter()) 
{ 
    serializer.WriteObject(writer, test); 
} 
// Add the iopbase prefix for clarity 
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "iopbase", "http://example.com/foo/")); 
Debug.WriteLine(doc); 

Um das Problem zu lösen, im Idealfall Sie möchten in der Lage sein, Namespaces für Datenelemente unabhängig vom Namespace des gesamten Datenvertrags anzugeben, aber leider ist der Serializer für Datenverträge doesn't support that. Stattdessen haben Sie folgende Möglichkeiten:

  • Sie Ihre SoapFaultException in eine Klassenhierarchie aufgeteilt könnten und einen anderen Namespace für jede Ebene in der Hierarchie festlegen, die am meisten abgeleiteten Typen im Namespace Root-Elemente platzieren wie so:

    [DataContract(Namespace = "")] 
    public abstract class SoapFaultExceptionBase 
    { 
        [DataMember(IsRequired = true)] 
        public string faultcode { get; set; } 
    
        [DataMember(IsRequired = true)] 
        public string faultstring { get; set; } 
    } 
    
    [DataContract(Namespace = "http://example.com/foo/")] 
    public class SoapFaultException : SoapFaultExceptionBase 
    { 
    } 
    
  • Sie könnten Ihre SoapFaultException im leeren Namensraum verlassen und construct your DataContractSerializer mit einem mitgelieferten XML-Stammelementnamen und Namespace:

    [DataContract(Namespace = "")] 
    public class SoapFaultException 
    { 
        [DataMember(IsRequired = true)] 
        public string faultcode { get; set; } 
    
        [DataMember(IsRequired = true)] 
        public string faultstring { get; set; } 
    } 
    

    Und dann tun:

    var serializer = new DataContractSerializer(typeof(SoapFaultException), "SoapFaultException", "http://example.com/foo/"); 
    var e = (SoapFaultException)serializer.ReadObject(xml.CreateReader()); 
    
+0

es jetzt offensichtlich scheint, dass Sie die untergeordneten Elemente zu einem Namensraum gehören nicht darauf hin! Ich habe Ihren Vorschlag für die Zusammenfassung und Unterklasse verwendet. Vielen Dank! – jameskind

Verwandte Themen