How to: c# XML InfoSet to JSON - c#

So WCF takes a JSON, and for whatever reason translates that to an XML Infoset (see: https://stackoverflow.com/a/5684703/497745). It then reads back this XML Infoset internally using the JsonReaderDelegator (see: https://referencesource.microsoft.com/#System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonReaderDelegator.cs,c0d6a87689227f04).
I am doing some very in-depth modification of the WCF execution flow, and I need to reverse the XML Infoset back to the original JSON.
Is there a library either in .NET or external that can take the XML Infoset generated by WCF and convert that back to its original JSON?

Found an answer that works in my scenario with some limited modifications.
This article: https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/18/wcf-extensibility-message-inspectors/ explains how to log the JSON that originally came in to WCF, even though it is only exposed as an XML InfoSet. This was the key insight. Specifically, look at its MessageToString implementation.
Below is the relevant portion of my code, based on the implementation of MessageToString in the above link, running within a class internal class MessageFormatInspector : IDispatchMessageInspector, IEndpointBehavior that I have written to inject into the WCF stack:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
...
var s = GetJsonFromMessage(request);
...
}
private static string GetJsonFromMessage(ref Message request)
{
using (MemoryStream ms = new MemoryStream())
{
using (XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(ms))
{
request.WriteMessage(writer);
writer.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray()); //extract the JSON at this point
//now let's make our copy of the message to support the WCF pattern of rebuilding messages after consuming the inner stream (see: https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.idispatchmessageinspector.afterreceiverequest(v=vs.110).aspx and https://blogs.msdn.microsoft.com/carlosfigueira/2011/05/02/wcf-extensibility-message-formatters/)
ms.Position = 0; //Rewind. We're about to make a copy and restore the message we just consumed.
XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max); //since we used a JsonWriter, we read the data back, we need to use the correlary JsonReader.
Message restoredRequestCopy = Message.CreateMessage(reader, int.MaxValue, request.Version); //now after a lot of work, create the actual copy
restoredRequestCopy.Properties.CopyProperties(request.Properties); //copy over the properties
request = restoredRequestCopy;
return json;
}
}
}
Unfortunately, the above code only works within the context of a WCF message inspector.
However, it is able to take an XMLDictionary, which has the XML InfoSet, and to using WCF's own built in JsonWriter to reverse the conversion WCF did and emit the original JSON.
I hope this helps someone save some time in the future.

Related

How to serialize an object into string\xml with its headers

I'm using a third side web service client (created by using the "Add service reference") in order to retrieve some data.
After filling the web service objects with proper data we need to add some data to the headers (encrypted password and some other predefined data)
Then, we are serializing every request sent to the web service, using the standard .net XmlSerializer.
However, in the result of the serialization I don't see the headers of the request. I've searched for a long time and couldn't find any way to "print" them as well.
Here is some example code:
Ibooking proxy = new BookingManager();
/* Init proxy Data...*/
GetAvailabilityRequest request = new GetAvailabilityRequest();
/*Fill more data on the request...*/
GetAvailabilityResponse response = proxy.GetAvailability(request); //Send request to the web service
var xmlString2 = response.Serialize(); //only body, no headers in the XML
/* Extension class to Serialize any object */
public static class ExtensionUtil
{
public static string Serialize<T>(this T value)
{
try
{
XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter))
{
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
}
I've excluded the code that adds more data to the request since it's long and complicated (need to implement IEndpointBehavior and IClientMessageInspector to "catch" the request before we send it) - but currently as a workaround I put a BreakPoint on the Message object and convert it into string using Visual Studio. In this way I do see the headers but obviously this is bad practice since I want it to be automated in the serialization.
I would like to see an example of how you are adding these headers.
In most web services the message body is the part that is serialized into XML or JSON - the headers are not.
You may be able to inspect the service call by using Fiddler and a proxy implemented by a small change in your web.config as described in this article: http://weblog.west-wind.com/posts/2008/Mar/14/Debugging-Http-or-Web-Services-Calls-from-ASPNET-with-Fiddler.
The short version of this is to add the following to your web.config or app.config:
<system.net>
<defaultProxy>
<proxy proxyaddress="http://127.0.0.1:8888" />
</defaultProxy>
</system.net>
Download and run Fiddler while calling the service and you should see and be able to inspect the call in Fiddler.
If you want to inspect and/or modify the headers within your code base could look into implementing IClientMessageInspector or IDispatchMessageInspector. Here are a couple articles on the topic:
https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.100).aspx
http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector
Here is an implementation I did. I didn't need access the headers, but rather to modify the xml namespaces created by the service client, but it should give you an idea on how to do the implementation: How can I create custom XML namespace attributes when consuming a legacy SOAP service?
OperationContext is your friend here. Use an OperationContextScope to wrap the call to the service, then use OperationContext.Current to get at all the hidden goodies you need.
https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontextscope(v=vs.110).aspx
Note that you'll need to know the specific types of the headers you want to get at, and I've had some trouble getting at the values, rather than just the names, of headers if they're not marked as serializable when using XmlSerializer

How to remove #strin3http//schemas.microsoft.com/2003/10/Serialization/� received from service bus queue received in python script?

I have set the output of Azure stream analytics job to service bus queue which sends the data in JSON serialized format. When I receive the queue message in python script, along with the data in curly braces, I get #strin3http//schemas.microsoft.com/2003/10/Serialization/� appended in front. I am not able to trim it as the received message is not being recognized as either a string or a message. Because of this I cannot de-serialize the data.
This TechNet article suggests the following code:
// Get indices of actual message
var start = jsonString.IndexOf("{");
var end = jsonString.LastIndexOf("}") + 1;
var length = end - start;
// Get actual message
string cleandJsonString = jsonString.Substring(start, length);
Pretty primitive but whatever works I suppose...
The issue was similiar with the SO thread Interoperability Azure Service Bus Message Queue Messages.
Per my experience, the data from Azure Stream Analytics to Service Bus was sent via AMQP protocol, but the protocol of receiving the data in Python is HTTP. The excess content was generated by AMQP during transmission.
Assumption that receiving the message via the code below, please see https://azure.microsoft.com/en-us/documentation/articles/service-bus-python-how-to-use-queues/#receive-messages-from-a-queue. The function receive_queue_message with the False value of the argument peek_lock wrapped the REST API Receive and Delete Message (Destructive Read).
msg = bus_service.receive_queue_message('taskqueue', peek_lock=False)
print(msg.body)
According to the source code of Azure Service Bus SDK for Python include the functions receive_queue_message, read_delete_queue_message and _create_message, I think you can directly remove the excess content from the msg.body using the string common function lstrip or strip.
I ran into this issue as well. The previous answers are only workarounds and do not fix the root cause of this issue. The problem you are encountering is likely due to your Stream Analytics compatibility level. Compatibility level 1.0 uses an XML serializer producing the XML tag you are seeing. Compatibility level 1.1 "fixes" this issue.
See my previous answer here: https://stackoverflow.com/a/49307178/263139.
I had the same issue but in a .net solution. I was writing a service which sends data to a queue, and on the other hand, I was writing a service which gets that data from the queue. I've tried to send a JSON, like this:
var documentMessage = new DocumentMessage();
var json = JsonConvert.SerializeObject(documentMessage);
BrokeredMessage message = new BrokeredMessage(json);
await _client.SendAsync(message);
In this second service I was getting the JSON but with this prefix:
#strin3http//schemas.microsoft.com/2003/10/Serialization/�
I solved this problem by add DataContractJsonSerializer like that:
var documentMessage = new DocumentMessage();
var serializer = new DataContractJsonSerializer(typeof(DocumentMessage));
BrokeredMessage message = new BrokeredMessage(documentMessage , serializer);
await _client.SendAsync(message);
If you want to solve the problem in that way, you will have to add Data Attributes from System.Runtime.Serialization to the model:
[DataContract]
public class DocumentMessage
{
[DataMember]
public string Property1 { get; private set; }
[DataMember]
public string Property2 { get; private set; }
}
When using Microsoft.ServiceBus nuget package, replace
message.GetBody<Stream>();
with
message.GetBody<string>();

Recreating a Message in WCF

I am retrieving data from a server via WCF (I am the client). Unfortunately, the server (which I have no control of) is sometimes returning invalid XML.
In order to fix this, I plan to add a IClientMessageInspector (on the client), which modifies the Message before WCF has chance to parse the returned XML.
My first step was to implement IClientMessageInspector, but have it so it leaves the response unchanged (it should effectively be a no-op), but for some reason it causes the generated WCF method (client.getBar() below) to return a null object, rather than a populated object.
class UTF8Policer : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Message revised = null;
var contents = new StringBuilder();
var writer = XmlWriter.Create(contents);
reply.WriteMessage(writer);
writer.Flush();
revised = Message.CreateMessage(reply.Version, reply.Headers.Action, XmlReader.Create(new StringReader(contents.ToString()));
revised.Headers.CopyHeadersFrom(reply);
revised.Properties.CopyProperties(reply.Properties);
reply = revised;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
However, when running:
var client = new Foo_RPCClient();
var header = new header();
// This is what registers the inspector
client.Endpoint.EndpointBehaviors.Add(new FooEndpointBehaviour());
var response = client.getBar(ref header, new BarRequest());
... response is null. If I comment out the inspector registration, response works.
My conclusion is therefore that I'm somehow invalidating the message within AfterReceiveReply. Can anyone advise what the correct way is to re-create the message received?
Once I've got this working, I'm hoping it'll be trivial to fixup the XML within AfterReceiveReply, so that it actually does something useful.
Ref parameter wont work with WCF.
When you do the call to the service everything is pass as an input message serialized. Then the service deserialize it and do the work and finally it return a serialized response to the client that is deserialized on the client side.
The ref parameter might compile but in no way you retain the reference to a memory pointer in the client computer. You must use the response to return back the object.

Error updating Wcf message before hitting service using IDispatchOperationSelector

We are having to intercept a SOAP message before it hits our WCF service to perform the following steps:
Route the message to the correct method as the client is unable to provide us with a SOAPAction value.
Update the namespaces of the xml as the client is unable to add namespace information to the message.
The routing is not an issue, but we are having a problem with creating the message; once we recreate the message the body merely consists of "... Stream ...".
Before creating the message, the messageContent variable contains valid, correct xml.
private Message UpdateNamespaces(Message message, string methodName)
{
var memoryStream = new MemoryStream();
var xmlWriter = XmlWriter.Create(memoryStream);
message.WriteMessage(xmlWriter);
xmlWriter.Flush();
var messageContent = Encoding.UTF8.GetString(memoryStream.ToArray());
xmlWriter.Close();
// Update messageContent with corrected XML
memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(messageContent));
var xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(memoryStream, new XmlDictionaryReaderQuotas());
var newMessage = Message.CreateMessage(xmlDictionaryReader, int.MaxValue, message.Version);
newMessage.Properties.CopyProperties(message.Properties);
return newMessage;
}
The messageContent is correct at the point at which we create the memoryStream, but as soon as I check the content of newMessage.ToString(), I'm getting the "... Stream ..." body content.
If anyone could help, I'd be very grateful as I'm out of ideas!
Many thanks
I think you are mixing behavior responsibilities.
IDispatchOperationSelector.SelectOperation is intended for routing decisions. It gives you a reference to the message, but the intention is to enable access to message properties. WCF does not expect the you to modify the message using this extension point. I can't say definitively, but the problem could be that simple.
If you want to alter the message namespace you should be using IDispatchMessageInspector. I suggest creating a second behavior for that task. Here's a nice Message Inspector example.

WCF Request body is sometimes a stream and sometimes buffered?

I have a WCF webservice. I am trying to do some logging whenever a request is received by implementing a MessageInspector and logging in the AfterReceiveRequest() event.
For some reason whenever I send a request to the webservice using the WCFTestClient.exe everything works fine. The message is logged and the request proceeds as normal.
But when I send a request to the webservice using SOAPUI as the client, making a copy of the request message causes the body to simply show <body>... stream ...</body> and it fails to be loaded as an XML document later for the sake of validation.
I'm guessing that a request from the WCFTestClient.exe is received with a buffered message body and a request from SOAPUI is received as a streamed body? How is this possible?
Is there someway I can write some code that will safely make a copy of either version? I have yet to figure out how to safely copy a streamed version as CreateBufferedCopy() obviously does not achieve this.
Or can I configure WCF to always create a buffered message body and never a stream?
Here is the code I am using to log and copy the request message:
object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
try
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message copy = buffer.CreateMessage();
LogRequest(copy);
ValidateMessage(ref request);
}
catch (Exception e)
{
throw new FaultException<>()...
}
return null;
}
The copy of the request message fails to be loaded into an XML document within the ValidateMessage() method if it came from SOAPUI with a streamed body. It succeeds to be loaded as an XML document if it comes from WCFTestClient.exe with a buffered body.
void validateMessage(ref System.ServiceModel.Channels.Message message)
{
XmlDocument bodyDoc = new XmlDocument();
//This load throws exception if request came from SOAPUI with streamed body...
bodyDoc.Load(message.GetReaderAtBodyContents());
...
}
The exception thrown by the Load() method is:
System.InvalidOperationException {"The specified node cannot be
inserted as the valid child of this node, because the specified node
is the wrong type."}
at System.Xml.XmlDocument.AppendChildForLoad(XmlNode newChild,
XmlDocument doc) at
System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc) at
System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean
preserveWhitespace) at System.Xml.XmlDocument.Load(XmlReader
reader) at ...
I believe that SOAPUI always sends the message requests it builds as Stream. I do not know for sure if this is something you can modify, either by code on your SOAPUI test or some SOAPUI configuration option/file on SOAPUI.
Ckeck the TransferMode property of your binding as explained here and here. You could possibly have multiple endpoints using different custom bindings for clients that send you buffered request and stremed requests.
Hope this helps.
What's the exception being thrown? The reader returned by GetReaderAtBodyContents() is positioned at the first element inside the body, not on the body tag itself. So the way you're loading your message is incorrect because the body could contain more than one node and in that case it will fail.
Just to check, could you use the following code to validate the content of the whole message (the copy) and see if the body contains exactly the same thing when sent from SOAPUI?
using (MemoryStream stream = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(stream))
{
message.WriteMessage(writer);
writer.Flush();
stream.Position = 0;
}
}
If you want all nodes inside the body you might have to create a Body node yourself.
The GetReaderAtBodyContents() method returns any characters between the closing element of the body and the closing element of the soap envelope. The XmlReader fails with the exception listed earlier in the thread when it reads past the closing element of the body.
More here:
http://www.katlaconsulting.co.uk/blog/wcfxmlschemavalidation

Categories