Поделиться через


Difficulties reading PSObject using WCF Message class

Issue: We would not be able to deserialize powershell object (PSObject) using WCF Message class directly.

Error:

"The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter schemas.xyz.com/Services/MyService:DoServiceResult. The InnerException message was 'Member 'CliXml' was not found.'.".

 

• Cause –
Failing Message:
<CliXml i:type="d3p1:string" xmlns:d3p1="www.w3.org/2001/XMLSchema">&lt;Objs Version="1.1.0.1" xmlns="schemas.microsoft.com/powershell/2004/04"&gt;

Working Message:
<CliXml xmlns:d7p1="www.w3.org/2001/XMLSchema" i:type="d7p1:string" xmlns="">&lt;Objs Version="1.1.0.1" xmlns="schemas.microsoft.com/powershell/2004/04"&gt;

Working message defines the namespace for the CliXml element where the failing version does not.

 

• Resolution –

We added a message inspector on the WCF services (not the client) and inspected the message in BeforeSendReply. It can also be compare by taking a service side WCF traces.

A simple workaround for this is to make a change similar to this:

MessageConverter.cs:

public Message ResponseToMessage(ResponseBase response)
{
try
{
var sb = new StringBuilder();

using (var sw = new StringWriter(sb))
using (var tw = new XmlTextWriter(sw))
{
var serializer = new DataContractSerializer(response.GetType(), BodyInfo.Operation + "Result", String.Empty);
serializer.WriteStartObject(tw, response);

tw.WriteAttributeString("xmlns", "b", null, OperationInfo.Service.Namespaces.Base); // "base" namespace
tw.WriteAttributeString("xmlns", "e", null, OperationInfo.Namespaces.Response ); // "entity" namespace

serializer.WriteObjectContent(tw, response);
serializer.WriteEndObject(tw);
}

var responseBody = String.Format("<{0} xmlns=\"{1}\">{2}</{0}>", BodyInfo.Operation + "Response", OperationInfo.Service.Namespaces.Service, sb.ToString());
responseBody = responseBody.Replace("<CliXml ", "<CliXml xmlns=\"\" ");
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(responseBody));

var responseAction = OperationInfo.Service.Namespaces.ReplyAction + BodyInfo.Operation + "Response";
return Message.CreateMessage(SoapMessage.Version, responseAction, new XmlNodeReader(doc));
}
catch (Exception ex)
{
throw new ApplicationException(String.Format("ResponseToMessage exception: '{0}'!", ex.Message), ex);
}
}

 

With this only change on the server side “ServiceHosting”, the “client” is able to read the “PSObject” information de-serialized perfectly fine.

 

Rohit Soni (MSFT)