views:

52

answers:

3

I am receiving an XML via an HttpPost to my service, and I want to log the received value for debugging purposes.

First I deserialize it into an entity like this:

XmlSerializer s = new XmlSerializer(typeof(TransactionResults));
TransactionResults t = (TransactionResults)s.Deserialize(stream);

This, of course, moves the stream to the end, so then I cannot log the original value.

I tried seeking to the beginning to then read it with a StreamReader, but it throws a NotSupportedException

As a test, I tried re-serializing it (I created all the objects again to avoid re using them to see if that was the problem)

private static string GetContents(TransactionResults t)
{
    XmlSerializer s = new XmlSerializer(typeof(TransactionResults));
    MemoryStream stream = new MemoryStream();
    s.Serialize(stream, t);
    return new StreamReader(stream).ReadToEnd();
}

This method returns an empty string.

(Obviously, if I invert the logic, the value gets logged, but then I cannot get the object)

What am I doing wrong? What would be the best way to deserialize the value into an object and log it as a string?

A: 

Read the stream into a memory stream and operate on that, it supports seeking.

Adam
+4  A: 

You should be able to seek to the beginning of a MemoryStream with no issues:

private static string GetContents(TransactionResults t)
{
    XmlSerializer s = new XmlSerializer(typeof(TransactionResults));
    MemoryStream stream = new MemoryStream();
    s.Serialize(stream, t);
    stream.Position = 0; // Rewind to the beginning of the stream
    return new StreamReader(stream).ReadToEnd();
}

If you want to log the original data instead, you'll effectively need to copy it into a MemoryStream to start with - then you can rewind it as often as you like.

I find it's useful to have a method like this to exhaust one stream, writing to another:

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8 * 1024];
    int len;
    while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, len);
    }    
}

You can call that passing in your service's input stream as the input, and a new MemoryStream as the output.

Jon Skeet
I thought I tried using a memorystream, *sigh*. Thanks!
Juan Manuel
This is probably the way to go, but just so another way is presented; you could also wrap any stream with another stream and locate/log values as the underlying stream is consumed (if memory is an issue)
Marc
@Marc, that way I'd need to deserialize it by hand instead of using the XmlSerializer (to do both things at the same time). But I agree it could be viable to save the creation of another stream. Memory is not an issue in my case though
Juan Manuel
Seems overly complex to use a MemoryStream to get a string, when the BCL provides a StringWriter class for getting string in this manor. If you already deserialized the object then you can get the XML from it easily.
Rodney Foley
@Juan: no sir, you simply pass the stream you created to the XmlSerializer and he consumes it. He's only aware he gets a stream, has no idea what else the stream does when he pulls from it ;)
Marc
@Creepy: If you're deserializing and then reserializing the data, you may well not end up with the same thing. Copying the data directly into a `MemoryStream` means you'll be able to see *exactly* what your service received, including any invalid data etc.
Jon Skeet
@Marc, oh, I misunderstood you. Yes, I did exactly that but with a memorystream to pass to the serializer, then seek to 0 and log it using a streamreader
Juan Manuel
A: 

This is the method I use when I need to get a Xml string from an XML serializable Object. So you first deserialize the object as you have been and then you get the string from the object, you will now have both the string and the original object.

public static string ToXmlString(object xmlObject)
{
    if (xmlObject == null)
        throw new NullReferenceException("xmlObject cannot be null.");

    String xmlResult = null;

    XmlSerializer serializer = new XmlSerializer(xmlObject.GetType());
    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, xmlObject);
        xmlResult = writer.ToString();
    }

    return xmlResult;
}
Rodney Foley
You won't necessarily have the *original* string sent by the client, however. For example, this will default to a UTF-16 encoding. It will also serialize with the *current* version of the code, which may not be what the client was using. If you want to see the original data, it's best to log it in as pristine a form as possible, IMO.
Jon Skeet
@Jon Good point, and I would agree with that. :)
Rodney Foley