question

MarkusFreitag-0088 avatar image
0 Votes"
MarkusFreitag-0088 asked AgaveJoe edited

C# parse soap response for elements and attribute values

Hello,
I have a SOAP C#Desktop application.
This can return an error.
Is there a way to simply parse this return string?
If yes, how?

I need ErrorNumber, Message, faultcode, faultstring
What do I have to consider when reading out? Namespace and so on

Thank you.

Works not

 XDocument doc = XDocument.Parse(xmlAnalyse);
                 XNamespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
    
                 //Grab the reader
                 var reader = xDoc.CreateReader();
                 //Set the root
                 var root = xDoc.Root;
                 //Use the reader NameTable
                 var namespaceManager = new XmlNamespaceManager(reader.NameTable);
    
                 IEnumerable<XElement> responses = doc.Descendants(ns + "OracleError");
                 foreach (XElement response in responses)
                 {
                     string strResponseErrorNumber = (string)response.Element(ns + "ErrorNumber");
                     string strResponseMessage = (string)response.Element(ns + "Message");
                 }


 <soap:Envelope
     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
     <soap:Body>
         <soap:Fault>
             <faultcode>soap:Client</faultcode>
             <faultstring>Error processing input</faultstring>
             <detail>
                 <OracleErrors
                     xmlns="http://xmlns.oracle.com/orawdsv/faults">
                     <OracleError>
                         <ErrorNumber>ORA-41821</ErrorNumber>
                         <Message>XML parsing failed</Message>
                     </OracleError>
                 </OracleErrors>
             </detail>
         </soap:Fault>
     </soap:Body>
 </soap:Envelope>

dotnet-csharpwindows-serverdotnet-aspnet-general
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

The WSDL should define the SOAP fault type. The standard pattern is casting the fault or using generics in the try...catch block.

0 Votes 0 ·

@MarkusFreitag-0088,

If the proposed solution is working for you, please don't forget to 'Accept Answer'.

0 Votes 0 ·
YitzhakKhabinsky-0887 avatar image
0 Votes"
YitzhakKhabinsky-0887 answered YitzhakKhabinsky-0887 commented

HI @MarkusFreitag-0088,

Please try the following solution.

c#

 void Main()
 {
     XDocument xdoc = XDocument.Parse(@"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
         <soap:Body>
             <soap:Fault>
                 <faultcode>soap:Client</faultcode>
                 <faultstring>Error processing input</faultstring>
                 <detail>
                     <OracleErrors xmlns='http://xmlns.oracle.com/orawdsv/faults'>
                         <OracleError>
                             <ErrorNumber>ORA-41821</ErrorNumber>
                             <Message>XML parsing failed</Message>
                         </OracleError>
                     </OracleErrors>
                 </detail>
             </soap:Fault>
         </soap:Body>
     </soap:Envelope>");
    
     string faultcode = xdoc.Descendants("faultcode").FirstOrDefault()?.Value;
     string faultstring = xdoc.Descendants("faultstring").FirstOrDefault()?.Value;
     Console.WriteLine("faultcode='{0}', faultstring='{1}'", faultcode, faultstring);
    
     XNamespace ns = "http://xmlns.oracle.com/orawdsv/faults";
     IEnumerable<XElement> OracleError = xdoc.Descendants(ns + "OracleError");
    
     foreach (XElement xelem in OracleError)
     {
         string strResponseErrorNumber = (string)xelem.Element(ns + "ErrorNumber")?.Value;
         string strResponseMessage = (string)xelem.Element(ns + "Message")?.Value;
         Console.WriteLine("ErrorNumber='{0}', Message='{1}'", strResponseErrorNumber, strResponseMessage);
     }
 }

Output

 faultcode='soap:Client', faultstring='Error processing input'
 ErrorNumber='ORA-41821', Message='XML parsing failed'
· 5
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thanks a lot, works well.

Last question.

  <faultcode>soap:Client</faultcode>
  <faultstring>Error processing input</faultstring>


When I need here also a namespace, however, how you would do it?

  string faultcode = xdoc.Descendants("faultcode").FirstOrDefault()?.Value;
   string faultstring = xdoc.Descendants("faultstring").FirstOrDefault()?.Value;


0 Votes 0 ·

Can you show me how you would read these values?
I should be able to solve the rest on my own. Thank you very much.

  • TIMESTAMP

  • RETURNDESCRIPTION



       <soap:Envelope
             xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
             <soap:Body>
                 <GET_PCB_IDOutput
                     xmlns="http://xmlns.oracle.com/orawsv/PDS/PS_PFWI">
                     <P_OUT>
                         <RESPONSE_SIE>
                             <MESSAGEID>f7de543c6-ad46-4230-831f-9e7777cf9e1b</MESSAGEID>
                             <TIMESTAMP>2022-01-27T16:07:19.387+01:00</TIMESTAMP>
                             <INTERFACEID>IF0FFF001</INTERFACEID>
                             <PROCESSINGSTATUS>
                                 <RETURNVALUE>REJECTED</RETURNVALUE>
                                 <RETURNCODE>PWS-2777</RETURNCODE>
                                 <RETURNDESCRIPTION>Receiver not running (WS Siemens App)</RETURNDESCRIPTION>
                             </PROCESSINGSTATUS>
                             <PROG/>
                         </RESPONSE_SIE>
                     </P_OUT>
                 </GET_PCB_IDOutput>
             </soap:Body>
         </soap:Envelope>
    



0 Votes 0 ·

@MarkusFreitag-0088,

It is a different question.
Please create a separate post for it.

1 Vote 1 ·

Thanks, it clear! The original question has been answered.

0 Votes 0 ·

@MarkusFreitag-0088,

Please post your 2nd question. I will reply right away.
And don't fall into a trap for retrieving a couple of XML tag values via deserialization with tons of code.

0 Votes 0 ·
cooldadtx avatar image
1 Vote"
cooldadtx answered MarkusFreitag-0088 commented

Please don't. Are you writing the TCP network calls to talk to your database? Are you hand building the web server hosting your site? If the answer is no then you see why hand parsing SOAP is always the wrong answer. It is complex, you aren't going to do it correctly and it'll be slow. .NET already supports SOAP out of the box. If you are mucking with XML or headers by hand you are doing it the hard way for no valid reason.

Use Connected Services in VS, point it to your WSDL. It'll auto-generate all the code to handle the call and report errors as exceptions. Use the generated client to send the SOAP request and get the response. If something goes wrong you get FaultException back that has all the information you are trying to parse by hand. It literally takes a couple lines of code to make this call compared to your multi-line code trying to parse the XML.

using (var client = new MyServiceClient())
{
    try
    {
       client.MakeSoapCall();
    } catch (FaultException e)
    {
       var code = e.Code;
       var message = e.Message;
       var reason = e.Reason;
    };

};

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

You are right, it is best to use a WSDL file.
My customer told me that an exception can occur, in which case I have to parse it myself. It's not easy.

 catch (FaultException ex)  // is not available!
 {
    
 }
 catch (Exception ex)
 {
     string error = ex.Message;
     string stackTrace = ex.StackTrace;

@YitzhakKhabinsky-0887, Thanks for your help, examples. I think many here could use your solutions.

0 Votes 0 ·
AgaveJoe avatar image
1 Vote"
AgaveJoe answered MarkusFreitag-0088 commented

I agree with cooldadtx, it's unusual to for a SOAP service to return a soap fault or response type that is not defined in the WSDL. The most likely scenario is you do not understand how the server works. Frankly, interacting with a SOAP service should take just a few minutes to get up and running.

Anyway, if you want to parse the XML into a type yourself, simply copy the XML. In Visual Studio select Edit -> Paste Special -> Paste XML as classes. Then use the standard XML serializer to populate a C# type. Anyway, this is what the proxy class does automatically except only the response type is return (the result of a SOAP action) where as the example below deserializes the the entire SOAP envelope (the XML you shared). You'll need to create code that maps the SOAP actions to the possible response types which is what the WSDL does...

https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer.deserialize?view=net-6.0


 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Xml.Serialization;
    
 namespace ConsoleApp1
 {
     class Program
     {
         static void Main(string[] args)
         {
             DeserializeObject("XMLFile1.xml");
         }
    
    
         private static void DeserializeObject(string filename)
         {
             // Create an instance of the XmlSerializer.
             XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
    
             // Declare an object variable of the type to be deserialized.
             Envelope e;
    
             using (Stream reader = new FileStream(filename, FileMode.Open))
             {
                 // Call the Deserialize method to restore the object's state.
                 e = (Envelope)serializer.Deserialize(reader);
             }
    
             // Write out the properties of the object.
             Console.WriteLine(e.Body.GET_PCB_IDOutput.P_OUT.RESPONSE_SIE.TIMESTAMP);
             Console.WriteLine(e.Body.GET_PCB_IDOutput.P_OUT.RESPONSE_SIE.PROCESSINGSTATUS.RETURNDESCRIPTION);
         }
     }
    
    
     // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
     [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.xmlsoap.org/soap/envelope/", IsNullable = false)]
     public partial class Envelope
     {
    
         private EnvelopeBody bodyField;
    
         /// <remarks/>
         public EnvelopeBody Body
         {
             get
             {
                 return this.bodyField;
             }
             set
             {
                 this.bodyField = value;
             }
         }
     }
    
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
     public partial class EnvelopeBody
     {
    
         private GET_PCB_IDOutput gET_PCB_IDOutputField;
    
         /// <remarks/>
         [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI")]
         public GET_PCB_IDOutput GET_PCB_IDOutput
         {
             get
             {
                 return this.gET_PCB_IDOutputField;
             }
             set
             {
                 this.gET_PCB_IDOutputField = value;
             }
         }
     }
    
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI")]
     [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI", IsNullable = false)]
     public partial class GET_PCB_IDOutput
     {
    
         private GET_PCB_IDOutputP_OUT p_OUTField;
    
         /// <remarks/>
         public GET_PCB_IDOutputP_OUT P_OUT
         {
             get
             {
                 return this.p_OUTField;
             }
             set
             {
                 this.p_OUTField = value;
             }
         }
     }
    
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI")]
     public partial class GET_PCB_IDOutputP_OUT
     {
    
         private GET_PCB_IDOutputP_OUTRESPONSE_SIE rESPONSE_SIEField;
    
         /// <remarks/>
         public GET_PCB_IDOutputP_OUTRESPONSE_SIE RESPONSE_SIE
         {
             get
             {
                 return this.rESPONSE_SIEField;
             }
             set
             {
                 this.rESPONSE_SIEField = value;
             }
         }
     }
    
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI")]
     public partial class GET_PCB_IDOutputP_OUTRESPONSE_SIE
     {
    
         private string mESSAGEIDField;
    
         private System.DateTime tIMESTAMPField;
    
         private string iNTERFACEIDField;
    
         private GET_PCB_IDOutputP_OUTRESPONSE_SIEPROCESSINGSTATUS pROCESSINGSTATUSField;
    
         private object pROGField;
    
         /// <remarks/>
         public string MESSAGEID
         {
             get
             {
                 return this.mESSAGEIDField;
             }
             set
             {
                 this.mESSAGEIDField = value;
             }
         }
    
         /// <remarks/>
         public System.DateTime TIMESTAMP
         {
             get
             {
                 return this.tIMESTAMPField;
             }
             set
             {
                 this.tIMESTAMPField = value;
             }
         }
    
         /// <remarks/>
         public string INTERFACEID
         {
             get
             {
                 return this.iNTERFACEIDField;
             }
             set
             {
                 this.iNTERFACEIDField = value;
             }
         }
    
         /// <remarks/>
         public GET_PCB_IDOutputP_OUTRESPONSE_SIEPROCESSINGSTATUS PROCESSINGSTATUS
         {
             get
             {
                 return this.pROCESSINGSTATUSField;
             }
             set
             {
                 this.pROCESSINGSTATUSField = value;
             }
         }
    
         /// <remarks/>
         public object PROG
         {
             get
             {
                 return this.pROGField;
             }
             set
             {
                 this.pROGField = value;
             }
         }
     }
    
     /// <remarks/>
     [System.SerializableAttribute()]
     [System.ComponentModel.DesignerCategoryAttribute("code")]
     [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI")]
     public partial class GET_PCB_IDOutputP_OUTRESPONSE_SIEPROCESSINGSTATUS
     {
    
         private string rETURNVALUEField;
    
         private string rETURNCODEField;
    
         private string rETURNDESCRIPTIONField;
    
         /// <remarks/>
         public string RETURNVALUE
         {
             get
             {
                 return this.rETURNVALUEField;
             }
             set
             {
                 this.rETURNVALUEField = value;
             }
         }
    
         /// <remarks/>
         public string RETURNCODE
         {
             get
             {
                 return this.rETURNCODEField;
             }
             set
             {
                 this.rETURNCODEField = value;
             }
         }
    
         /// <remarks/>
         public string RETURNDESCRIPTION
         {
             get
             {
                 return this.rETURNDESCRIPTIONField;
             }
             set
             {
                 this.rETURNDESCRIPTIONField = value;
             }
         }
     }
    
    
 }

Results

 1/27/2022 10:07:19 AM
 Receiver not running (WS Siemens App)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thanks looks complicated I think @YitzhakKhabinsky-0887 solution is simpler, more understandable.

0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered AgaveJoe edited

I disagree the XDocument example is a simpler solution. I feel strong types are a more maintainable approach than strings. Above all I recommend following standard SOAP service patterns in .NET. It does not make a lot of sense to manually parse a SOAP response when the framework has well-tested libraries that handle SOAP.

Anyway, based on the previous example...

 static void Main(string[] args)
 {
     XDocument xdoc = XDocument.Parse(@"<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
     <soap:Body>
         <GET_PCB_IDOutput xmlns=""http://xmlns.oracle.com/orawsv/PDS/PS_PFWI"">
             <P_OUT>
                 <RESPONSE_SIE>
                     <MESSAGEID>f7de543c6-ad46-4230-831f-9e7777cf9e1b</MESSAGEID>
                     <TIMESTAMP>2022-01-27T16:07:19.387+01:00</TIMESTAMP>
                     <INTERFACEID>IF0FFF001</INTERFACEID>
                     <PROCESSINGSTATUS>
                         <RETURNVALUE>REJECTED</RETURNVALUE>
                         <RETURNCODE>PWS-2777</RETURNCODE>
                         <RETURNDESCRIPTION>Receiver not running (WS Siemens App)</RETURNDESCRIPTION>
                     </PROCESSINGSTATUS>
                     <PROG/>
                 </RESPONSE_SIE>
             </P_OUT>
         </GET_PCB_IDOutput>
     </soap:Body>
 </soap:Envelope>");
    
    
     XNamespace ns = "http://xmlns.oracle.com/orawsv/PDS/PS_PFWI";
     string ts = xdoc.Root.Descendants(ns + "TIMESTAMP").FirstOrDefault().Value;
     string desc = xdoc.Root.Descendants(ns + "RETURNDESCRIPTION").FirstOrDefault().Value;
     Console.WriteLine($"{ts}\r\n{desc}");
 }

Results

 2022-01-27T16:07:19.387+01:00
 Receiver not running (WS Siemens App)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.