WCF IDispatchMessageInspector How to fix incoming xml? - c#

In the WCF service, I have implemented the IDispatchMessageInspector interface.
In the AfterReceiveRequest method ref Message request - the error "when reading the body: System.Xml.XmlException" comes. This error occurs due to an error in the XML in the request.I can't influence the request.
<data xsi:type="xsd:string"><?xml version="1.0" encoding="utf-8"?>
<someRequest>
<Number>Test</Number>
<Date>2023-01-09T00:00:00</Date>
</someRequest>
I'm trying to fix
request.toString().Replace("<?xml version="1.0" encoding="utf-8"?>" ,"");
Is it possible to get the xml body as text?
Tried:
using (var reader = request.GetReaderAtBodyContents())
{
query string var = reader.ReadContentAsString();
}
var body = request.getBody<string>();
If I implement the IDispatchOperationSelector interface. The method is called before AfterReceiveRequest. But there's also a Message parameter

To read an xml file, you can use XmlDocument.InnerXml to get it :
XmlDocument doc = new XmlDocument();
doc.Load("path to your file");
string xmlcontents = doc.InnerXml;
Or like this:
public string GetXMLAsString(XmlDocument myxml)
{
StringWriter sw = new StringWriter();
XmlTextWriter tx = new XmlTextWriter(sw);
myxml.WriteTo(tx);
string str = sw.ToString();//
return str;
}
After getting it, then get rid of what you don't need.

Related

Exception in Deserialize xml to c# object

In my application, i have serialized c# object to xml and passed xml to API to generate data and got the reponse xml in return as expected as below,
<?xml version="1.0" encoding="utf-8"?>
<SaveLockResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.easy2access.no/webservice/types">
<Data xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true"/>
<Header>New lock was created</Header>
<Message>A lock with serialnumber [23-215-038-028476] was successfully created for customer [28242].</Message>
<Status>Success</Status>
<Lock xmlns:d2p1="http://schemas.datacontract.org/2004/07/Easy2Access.Engine.Engine.Types">
<d2p1:CustomerNumber>28242</d2p1:CustomerNumber>
<d2p1:Description>String</d2p1:Description>
<d2p1:G3LockId>0</d2p1:G3LockId>
<d2p1:LockId>28158</d2p1:LockId>
<d2p1:LockType>G2</d2p1:LockType>
<d2p1:MultiCode>String</d2p1:MultiCode>
<d2p1:OnetimeCode>String</d2p1:OnetimeCode>
<d2p1:SerialNumber>23-33-44-02846</d2p1:SerialNumber>
</Lock>
</SaveLockResponse>
Now i want this to be converted back to c# object, And i follow the below code,
public static T DeserializeFromXml<T>(string xml)
{
T result;
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader tr = new StringReader(xml))
{
result = (T)ser.Deserialize(tr);
}
return result;
}
When i call this method i will get error as below,
There is an error in XML document (1, 40).And inner exception as
{"http://www.easy2access.no/webservice/types'> was not expected."}
Any suggesions most welcome!.
Regards
Sangeetha

Creating variable from XML localhost:8080 source in c#

Ive been using the following code to call up data coming from localhost:8080 in xml format. This part of the code works fine:
private void openP()
{
String sUrl = "http://localhost:8080/GetOpen=";
XmlTextReader rssReader = new XmlTextReader(sUrl.ToString());
XmlDocument rssDoc = new XmlDocument();
WebRequest wrGETURL;
wrGETURL = WebRequest.Create(sUrl);
Stream objStream;
objStream = wrGETURL.GetResponse().GetResponseStream();
StreamReader objReader = new StreamReader(objStream, Encoding.UTF8);
WebResponse wr = wrGETURL.GetResponse();
}
I want to know how I can now use the data that xhttp://localhost:8080/GetOpen= returns as a variable in my program.
for example if I were to goto xhttp://localhost:8080/GetOpen= in a web browser I would see this:
<Response>
<Content>
<Position Symbol="xVAR" Market="blah" >
</Content>
</Response>
how would I go about making xVar a String variable that I can just simply write to a label?
Thanks
Since you're using XmlDocument, then you can use SelectSingleNode() method passing correct XPath to get particular part of the XML :
rssDoc.Load("http://localhost:8080/GetOpen=");
.....
XmlNode symbol = rssDoc.SelectSingleNode("//Position/#Symbol");
String symbolValue = "";
if(symbol != null) symbolValue = symbol.Value;

Deserializing WCF Message at Server

I implemented a custom message inspector (via IDispatchMessageInspector) to intercept messages that are received on the server side of a WCF service so I can attempt to deserialize the message and apply some specific business logic. The problem I'm encountering is when I write the MessageBuffer's contents to a new MemoryStream and then try to deserialize, I get an error that says "The data at the root level is invalid. Line 1, position 1." I do know the data being passed in is valid as skipping over the inspector makes everything work fine.
Sample Code:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
string msg = buffer.CreateMessage().ToString();
var dc = new DataContractSerializer(typeof(Adder));
using (var stream = new MemoryStream())
{
buffer.WriteMessage(stream);
stream.Position = 0;
//deserializing error occurs here
var c = dc.ReadObject(stream);
}
return null;
}
Here is the Adder class/interface:
[DataContract(Name = "adder", Namespace = "http://test.com")]
public class Adder
{
[DataMember(Name = "first")]
public int First { get; set; }
[DataMember(Name = "second")]
public int Second { get; set; }
}
[ServiceContract(Namespace = "http://test.com")]
public interface ITestSvc
{
[OperationContract(Name = "add")]
int Add(Adder adder);
}
Any suggestions or is there a better option for this? My main goal is to read the XML (in a deserialized object) on every WCF request that comes into my service.
The request object contains the WCF message headers as well as the payload. You'll need to strip off the headers and then you should be able to deserialize the message body.
For example, a SOAP message would have:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
</soap:Header>
<soap:Body>
<!-- your payload -->
</soap:Body>
You could use XML navigation to get to the body element and then you'd deserialize that element alone.
EDIT:
Actually just stumbled on this method which I think should do the trick for you:
Message.GetReaderAtBodyContents
I just did this. Just paste all of the following code into your class, and call DeserializedResponse(). You'll need to change MyResponseObject to the name of whatever object you are trying to deserialize. Also, you will need to replace "_requestInspector.Response" with your own response xml string variable. The method GetSoapBodyInnerXml() will strip off the Soap Envelope, and only return your response xml which you wish to deserialize.
private MyResponseObject DeserializedResponse()
{
var rootAttribute = new XmlRootAttribute("MyResponseObject ");
rootAttribute.Namespace = #"http://www.company.com/MyResponseObjectNamespace";
XmlSerializer serializer = new XmlSerializer(typeof(MyResponseObject ), rootAttribute);
string responseSoapBodyInnerXml = GetSoapBodyInnerXml(_requestInspector.Response);
AddXmlDeclaration(ref responseSoapBodyInnerXml);
MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(responseSoapBodyInnerXml));
MyResponseObject resultingResponse = (MyResponseObject )serializer.Deserialize(memStream);
return resultingResponse;
}
private string GetSoapBodyInnerXml(string soapMessage)
{
XDocument xDoc = XDocument.Parse(soapMessage);
XNamespace nsSoap = #"http://schemas.xmlsoap.org/soap/envelope/";
return xDoc.Descendants(nsSoap + CONST_SoapBody).Descendants().First().ToString();
}
private void AddXmlDeclaration(ref string xmlString)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
//Create an XML declaration.
XmlDeclaration xmldecl;
xmldecl = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
//Add the new node to the document.
XmlElement root = doc.DocumentElement;
doc.InsertBefore(xmldecl, root);
//Return updated xmlString with XML Declaration
xmlString = doc.InnerXml;
}
I ended up going the message header route, like Mike Parkhill suggested.

XmlDocument in WCF Service method not successfully saving to file using the class' save method

Hello and thanks in advance,
I am attempting to take the input from text boxes in a silverlight application and on an event fired by a button click, convert them to an xml string, pass the string and a specified file name to a WCF service call and in that call save the xml to the specifed file(via a string parameter). The code which captures the text into an xml string seems to be successfully working(based on what I see in the variables when debugging) and looks like this:
private void ServerInfoNext_Click(object sender, RoutedEventArgs e)
{
//new RegisterServerGroupObject instance
RegisterServerGroupObject groupInfo= new RegisterServerGroupObject(groupNameTB.Text,1,parentServerNameTB.Text,LeaderNameCB.SelectedItem.ToString());
var serializer = new XmlSerializer(typeof(RegisterServerGroupObject));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.CloseOutput = true;
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb,settings))
{
serializer.Serialize(writer, groupInfo);
writer.Close();
}
//sb now contains the xml string with the information from the serialized class
string contentsString = sb.ToString();
//create instance of XmlWrite service
XMLWriteServiceClient xmlClient = new XMLWriteServiceClient();
xmlClient.WriteXmlToServerCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(xmlClient_WriteXmlToServerCompleted);
xmlClient.WriteXmlToServerAsync("ServerGroups.xml", contentsString);
}
at this point when the variable contents string is passed to the service method, I can see that it has valid xml, as well as within the service method itself, which looks like this:
public class XMLWriteService : IXMLWriteService
{
public void WriteXmlToServer(string filename,string xmlString)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xmlString.ToString());
try
{
xDoc.Save(filename);
}
catch (FileNotFoundException e)
{
Console.WriteLine(e.InnerException.ToString());
}
}
}
The try/catch block is not indicating that the file("ServerGroups.xml") is not found, and I currently have that xml file in the ClientBin of the server side portion of the project. (the .Web side). However, after the method terminates there is no new xml written to the file. Can someone please tell me what I am doing wrong? I don't know why the XmlDocument class instance is not saving its contents to the file. Thanks in advance!
You aren't passing a path, so it's just going to save the file to the current directory of the WCF service process, whatever that happens to be. Either find out what that is, or do a search on your whole server drive for that file name to see where it's saving it. Better yet, call Path.Combine to append a path to the begining of the file name before you save to it. For instance:
xDoc.Save(Path.Combine("C:\\ClientBin", filename));
To answer your question in the comment below, if you want to append the incoming XML data to the data that is already stored in the XML file on the server, that's a bit more involved. It all depends what the format of the XML is. Since you are using serialization, which by default will only allow one object per XML document (because it puts the object name as the root document element, of which there can only be one), then you would have to have a different XML format. For instance, on the server side, you would need to have some kind of root element on the document under which you could keep appending the incoming RegisterServerGroupObject objects. For instance, if your XML file on the server looked like this:
<?xml version="1.0" encoding="utf-8" ?>
<ListOfRegisterServerGroupObject>
</ListOfRegisterServerGroupObject>
Then, you could append the data by inserting new elements within that root element, like this:
<?xml version="1.0" encoding="utf-8" ?>
<ListOfRegisterServerGroupObject>
<RegisterServerGroupObject>
...
</RegisterServerGroupObject>
<RegisterServerGroupObject>
...
</RegisterServerGroupObject>
...
</ListOfRegisterServerGroupObject>
To do this, you would need to first load the XML document, then get the root element, then append the incoming XML as a child element. For instance:
public void WriteXmlToServer(string filename, string xmlString)
{
string filePath = Path.Combine("C:\\ClientBin", filename);
XmlDocument storage = New XmlDocument();
storage.Load(filePath);
XmlDocument incoming = New XmlDocument();
incoming.LoadXml(xmlString);
storage.DocumentElement.AppendChild(incoming.DocumentElement);
storage.Save(filePath);
}
You may need to 'map' the physical path to the output file within the service
string path = HostingEnvironment.MapPath("~/MyPath/MyFile.xml");

XmlSerializer - Encoding not supported

I have the following extension method to serialize my class....
public static string SerializeToXml<T>(this object obj)
{
XDocument doc = new XDocument();
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var writer = doc.CreateWriter())
{
ser.Serialize(writer, obj);
}
return doc.ToString();
}
This seems to work fine and returns the following string for my serialized object:
<AuthenticatedUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Username>mark</Username>
<UserID>4</UserID>
<Roles>
<string>AuthenticatedUsers</string>
</Roles>
<IsValid>false</IsValid>
</AuthenticatedUser>
However when I try to deserialize this string using the method below I get this error:
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsMale>false</IsMale>\r\n</AuthenticatedUser>' is not valid for this call because this XmlSerializer instance does not support encoding. Use the SoapReflectionImporter to initialize an XmlSerializer that supports encoding."}
Method....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
So after I read the error message I changed the deserialize method to use the SoadReflectionImporter....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
SoapReflectionImporter soap = new SoapReflectionImporter();
var mapping = soap.ImportTypeMapping(typeof(T));
XmlSerializer ser = new XmlSerializer(mapping);
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
However I now get this error...
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsValid>false</IsValid>\r\n</AuthenticatedUser>' is not valid for this call. Valid values are 'http://schemas.xmlsoap.org/soap/encoding/' for SOAP 1.1 encoding or 'http://www.w3.org/2003/05/soap-encoding' for SOAP 1.2 encoding."}
Does anyone know where I'm going wrong and how I can deserialize this string successfully?
The problem is the overload of the Deserialize method that you are calling:
return (T)ser.Deserialize(reader, xml);
The xml parameter in the call specifies the encoding style, but in this case you are passing the xml from the serialization. Simply delete the second parameter and just call Deserialize with the reader and it should work fine:
return (T)ser.Deserialize(reader);
XElement.CreateReader() doesn't return the XDeclaration.
Instead, try making an XmlReader from a StringReader.
Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, you should be using DataContractSerializer
Here is a nice blog post about the two
Do you need the Parse(xml) call and the reader element? Since you have the string, can't you just deserialize the string? First convert to bytes...
byte [] bytes = Encoding.Unicode.GetBytes(xml);
MemoryStream mem = new MemoryStream(bytes);
returnValue = (T)ser.Deserialize(mem);

Categories