Getting the details of an ONVIF FaultException - c#

This answer explains how to get the text of a SOAP FaultException, but it only works when the contents are a serialised string, resulting in <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">details</string>.
This ONVIF device howevers uses the SOAP <env:Text> element, which can't be deserialised to a string.
How can I read the detaisl of the following FaultException?
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:enc="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:rpc="http://www.w3.org/2003/05/soap-rpc" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing">
<env:Header>
<wsa:Action>http://www.w3.org/2005/08/addressing/soap/fault</wsa:Action>
</env:Header>
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Receiver</env:Value>
<env:Subcode>
<env:Value>ter:Action</env:Value>
</env:Subcode>
</env:Code>
<env:Reason>
<env:Text xml:lang="en">ActionFailed</env:Text>
</env:Reason>
<env:Detail>
<env:Text>It is not possible to operate because of the unsupported video source mode.</env:Text>
</env:Detail>
</env:Fault>
</env:Body>
</env:Envelope>
This code is from the linked answer above:
} catch (FaultException ex) {
System.ServiceModel.Channels.MessageFault mf = ex.CreateMessageFault();
if (mf.HasDetail) {
string detailedMessage = mf.GetDetail<string>();
output.OutputText(detailedMessage);
}
}
Which fails with:
An exception of type
'System.Runtime.Serialization.SerializationException' occurred in
System.Runtime.Serialization.dll but was not handled in user code
Additional information: Expecting element 'string' from namespace
'http://schemas.microsoft.com/2003/10/Serialization/'.. Encountered
'Element' with name 'Text', namespace
'http://www.w3.org/2003/05/soap-envelope'.
There must be a built in deserialiser for it as the the <env:Reason> element uses the same node.

You can get the detail as a "raw" XmlElement:
System.ServiceModel.Channels.MessageFault mf = ex.CreateMessageFault();
if (mf.HasDetail) {
System.Xml.XmlElement detailedMessage = mf.GetDetail<System.Xml.XmlElement>();
System.Diagnostics.Trace.WriteLine("Detail: " + detailedMessage.InnerText);
}
This should only be used when you know what the contents will be, unless wrapped in a try { } catch { } block

Related

C# Deserilize from xml to object (<result xmlns=''> was not expected.)

I try to desalinize from XML to object. When i try to execute this code i get this inner exception get error message was not expected.. Please help me. Thank you for all reply.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<status>
<interfaceId>shop.shipping.segment.get</interfaceId>
<systemStatus>OK</systemStatus>
<message>OK</message>
<requestId>714a4983-555f-42d9-aeea-89dae89f2f55</requestId>
<requests>
<id>1</id>
<kbnId>1</kbnId>
</requests>
</status>
<tns:shopMngApiResponse xmlns:tns="http://rakuten.co.jp/rms/mall/shop/mng/api/model/resource">
<resultCode>N000</resultCode>
<resultMessageList>
<resultMessage>
<code>N000</code>
<message>Succeeded.</message>
</resultMessage>
</resultMessageList>
<result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="tns:soryoKbnResourceModel">
<soryoKbnList>
<soryoKbn>
<shopId>202317</shopId>
<kbnId>1</kbnId>
<id>1</id>
<name>test name 1</name>
</soryoKbn>
<soryoKbn>
<shopId>202317</shopId>
<kbnId>2</kbnId>
<id>7</id>
<name>test name 2</name>
</soryoKbn>
</soryoKbnList>
</result>
</tns:shopMngApiResponse>
</result>
Source Code: https://app.box.com/s/mmuk2ndkmz4llb71ryw81rpzca9mrmge
When deserializing into the "result"- object set the property as following:
[XmlElement(ElementName = "result", Form = XmlSchemaForm.Qualified)]
public YOURRESULTOBJECT result { get; set; }

reading an xml file in C# using CsQuery

using CsQuery;
namespace CSQuery
{
class Program
{
static void Main(string[] args)
{
var dom = CQ.Create(/*I am not sure what goes here*/);
//Not sure if this is the correct setup as well
CQ mf = dom["MANUFACTURER"];
CQ md = dom["MODEL"];
Console.WriteLine(mf);
Console.WriteLine(md);
Console.ReadKey();
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
--------------------------------------------------------
PARTS:
Title: Computer Parts
<ITEM>Motherboard</ITEM>
<MANUFACTURER>ASUS</MANUFACTURER>
<MODEL>P3B-F</MODEL>
<COST> 123.00</COST>
<ITEM>Video Card</ITEM>
<MANUFACTURER>ATI</MANUFACTURER>
<MODEL>All-in-Wonder Pro</MODEL>
<COST>160.00</COST>
<ITEM> Monitor </ITEM>
<MANUFACTURER>LG Electronics</MANUFACTURER>
<MODEL> 995E</MODEL>
<COST> 290.00</COST>
</PART></PARTS>
Above is my code I have written so far I am trying to extract the MANUFACTURER and MODEL from the sample given xml code. When I compile I get an error message saying source can not be found and I think it may be a problem with my setup and I am unclear on what exactly is suppose to go into my CQ.Create() as a parameter(I tried putting in the exact parts.xml file but that didn't help).

C# CreateDocumentType for non-PUBLIC

I want to get the following output:
<?xml version="1.0" encoding="Windows-1252"?>
<!DOCTYPE SUPRMRT SYSTEM "suprmrt.dtd">
I have the following code:
XmlDocument doc = new XmlDocument();
XmlDocumentType docType = doc.CreateDocumentType("SUPRMRT", "SYSTEM", "suprmrt.dtd", null);
doc.AppendChild(docType);
doc.Save(Console.out);
This produces:
<?xml version="1.0" encoding="Windows-1252"?>
<!DOCTYPE SUPRMRT PUBLIC "SYSTEM" "suprmrt.dtd">
So my question is can I get a result where PUBLIC is replaced by SYSTEM? Also, if I replace "SYSTEM" with null, I get a set of empty quotes. Can I stop that from happening?
Write it like this.
XmlDocumentType docType = doc.CreateDocumentType("SUPRMRT", null, "suprmrt.dtd", null);
Here is the MSDN Documentation
publicId
Type: System.String
The public identifier of the document type or null. You can specify a
public URI and also a system identifier to identify the location of
the external DTD subset.

C# Webservice to return entire SOAPBody as XML

I'm patching together a C# Webservice which reads predefined SOAP responses from XML files. We're using this to mock a productive web service. The XML files contain the nodes directly child to ''. However, my current code adds a node to '', thus resulting in a wrong format.
What I got:
<soap:Body>
<Security_AuthenticateResponse xmlns="http://xml.tempuri.com/">
<Security_AuthenticateReply xmlns="http://xml.tempuri.com/VLSSLR_06_1_1A">
<processStatus>
...
</processStatus>
</Security_AuthenticateReply>
</Security_AuthenticateReply>
</soap:Body>
What we need:
<soap:Body>
<Security_AuthenticateReply xmlns="http://xml.tempuri.com/VLSSLR_06_1_1A">
<processStatus>
...
</processStatus>
</Security_AuthenticateReply>
</soap:Body>
My code:
[SoapDocumentMethod(Action = "http://webservices.amadeus.com/1ASIWABOAAI/VLSSLQ_06_1_1A",
RequestNamespace = "http://xml.amadeus.com/VLSSLQ_06_1_1A",
RequestElementName = "Security_Authenticate",
ResponseNamespace = "http://xml.amadeus.com/VLSSLR_06_1_1A",
ResponseElementName = "Security_AuthenticateReply")]
[WebMethod]
[return: XmlAnyElement]
[SoapHeader("sessionId", Direction = SoapHeaderDirection.Out)]
public XmlDocument Security_Authenticate()
{
.. some file magic here ...
XmlDocument rp = getFile("Security_AuthenticateReply.xml");
return rp;
}
I'm hoping for some directive which tells C# to drop one of the nodes. As an alternative, I guess I'll have to extract all child nodes and return those.

Parsing URL/web-service

I made a request to a third party API and it gives me the following response in XML.
<?xml version="1.0" ?>
<abc>
<xyz>
<code>-112</code>
<message>No such device</message>
</xyz>
</abc>
I read this using this code.
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/abc/xyz");
string code = node.SelectSingleNode("code").InnerText;
string msg = node.SelectSingleNode("message").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
But I get an error on this line:
string code = node.SelectSingleNode("code").InnerText;
Error is:
Object reference not set to an instance of an object.
I changed the first line of your XML file into:
<?xml version="1.0"?>
to make it valid XML. With this change, your code works for me. Without the change, the parser throws an exception.
You can use LINQ to XML (if confortable):
XDocument doc = XDocument.Load(url);
var selectors = (from elements in doc.Elements("abc").Elements("xyz")
select elements).FirstOrDefault();
string code = selectors.Element("code").Value;
string msg = selectors.Element("message").Value;
As you've given it, there doesn't seem to be anything wrong with your code Edit : Your declaration is wrong, as svinja pointed out, and your xml won't even load into the XmlDocument.
However, I'm guessing that your xml is more complicated, and there is at least one namespace involved, which would cause the select to fail.
It isn't pretty, but what you can do is use namespace agnostic xpath to locate your nodes to avoid using a XmlNamespaceManager:
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/*[local-name()='abc']/*[local-name()='xyz']");
string code = node.SelectSingleNode("*[local-name()='code']").InnerText;
string msg = node.SelectSingleNode("*[local-name()='message']").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
Edit - Elaboration
Your code works fine if you correct the declaration to <?xml version="1.0"?>
However, if you introduce namespaces into the mix, your code will fail unless you use namespace managers appropriately.
My agnostic xpath above will also parse an xml document like so:
<?xml version="1.0"?>
<abc xmlns="foo">
<xyz xmlns="bar">
<code xmlns="bas">-112</code>
<message xmlns="xyz">No such device</message>
</xyz>
</abc>
<?xml version="1.0">
<abc>
<xyz>
<code>-112</code>
<message> No such device </message>
</xyz>
</abc>
try to set a list:
XmlNodeList nodeList = root.SelectNodes("/abc/xyz");
then read all the nodes and get their text:
foreach(XmlNode node in nodeList)
{
if(node.Name == "code")
{
string code = node.InnerText;
}
else
if(node.Name == "message")
{
string msg = node.InnerText;
}
}
[XmlRoot("abc")]
public class Entity
{
[XmlElement("xyz")]
public SubEntity SubEntity { get; set; }
}
public class SubEntity
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("message")]
public string Message { get; set; }
}
And use standart xmlserializer
var xmlSerializer = new XmlSerializer(typeof(Entity));
var result = xmlSerializer.Deserialize(new XmlTextReader("*** url ***"));
Response.Write("Code: " + result.SubEntity.Code);
Response.Write("Message: "+ result.SubEntity.Message);

Categories