Yossi Dahan [BizTalk]

Google
 

Saturday, February 21, 2009

Serialisation, mixed content and string[]

When you generate a class out of a schema with an element configured to allow mixed content (child attributes and elements as well as text), you should expect the corresponding generated field type to be a string array;

So - if you have a schema that looks like this

<?xml version="1.0" encoding="utf-8"?>
<
xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<
xs:element name="SomeElement">
<
xs:complexType mixed="true">
<
xs:sequence>
<
xs:element name="Child1" type="xs:string"/>
<
xs:element name="Child2" type="xs:string"/>
<
xs:element name="Child3" type="xs:string"/>
</
xs:sequence>
<
xs:attribute name="SomeAttribute" type="xs:string"/>
</
xs:complexType>
</
xs:element>
</
xs:schema>

(‘SomeElement’ being a complex type allowing mixed content)

The fields in the generated class would look like

public partial class SomeElement {

private string child1Field;

private string child2Field;

private string child3Field;

private string[] textField;

private string someAttributeField;
.
.
.

The reason for the array of strings (instead of just one string field) is that an XML corresponding to the schema might look like this –


<SomeElement xmlns="http://tempuri.org/XMLSchema.xsd" SomeAttribute="someAttributeValue">
Some free text
<
Child1>Child1 text</Child1>
Some more free text
<
Child2>Child2 text</Child2>
yet some more free text
<
Child3>Child3 text</Child3>
</
SomeElement>

And so by using a string array to hold the text the deserialiser can keep string portions separately.

Initially, I thought, this allows the structure to represent the original xml accurately, but this is not exactly the case – you would still not know for certain where each string portion existed, especially if in the source XML you get a few elements that don’t have text between them, which , I suspect, is why when I serialise the instance back to xml I actually get –

<?xml version="1.0"?>
<
SomeElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SomeAttribute="someAttributeValue" xmlns="http://tempuri.org/XMLSchema.xsd">
<
Child1>Child1 text</Child1>
<
Child2>Child2 text</Child2>
<
Child3>Child3 text</Child3>
Some free text

Some more free text

yet some more free text
</
SomeElement>

Now, I don’t particularly like this sort of xml, and shy away from mixed content; I don’t believe that xml snippets like my samples above are useful, specifically I don’t think that mixing elements and text is particularly nice.


However, consider an element with an attribute and some text – the following is quite reasonable I think, and yet requires mixed content -


<Phone type="mobile">some text here</Phone>



Labels: , ,

Wednesday, December 12, 2007

Exception, Orchestration, Serialisation.

I was adding a custom exception yesterday to a helper class I’m calling from an orchestration.

Usually, my exception handlers in the orchestration are quite short; this time, however, I wanted to do a bit more, which included calling a web service when a particular expcetion is caught.

While implementing this I learnt something interesting (which, arguably, I should have known a long time ago – just to show how difficult it is to catch up on all the changes in the .net framework) -

In.net framework 2.0 a Data property of type IDictionary was added to the Exception class, which by it's own is not a problem, only that IDictionary is not serialisable and so could have proved rather difficult to anyone using Exception, especially in a BizTlak environment.

Luckily (but not surprisingly) the .net framework team have implemented ISerializable in the Exception class, which helps, but does cause a small headake to the unexpecting BizTalk developer (me).

But first - I have to apologise - again I'm not familiar with all the details around this, and am resorting to pure guesses of a couple of points (will be happy to get more information if you care to enlighten me), still - I'm sure this will be useful to most people...

When you mark a class as [Serializable], as the runtime deserialises a class it attempts to call a parameterless constructor to create an instance of the type; the serialiser will then populates all the members of the class through their public properties (I suspect that this is, partly at least, why Xml Serialisation serialises public members only).

When working with ISerializable, however, the runtime expects a constructor that takes SerializationInfo and StreamingContext as parameters; it is expected that the constructor will populate the members out of the SerializationInfo collection.

I believe that the runtime interrogates the type to be deserialised and, once it finds that the type or any type in its inheritance path implements ISerializable it takes the second approach mentioned.

Not realising the Exception class implements ISerializable ,I did not have the expected constructor in my class, which meant that when BizTalk tried to deserialise the object (between the send shape calling the web service and the receive shape expecting the response) it failed, which now exaplains the error reported in the event log -

The constructor to deserialize an object of type ‘[custom exception class name here]’ was not found.


Adding the constructor with the two parameters to my custom exception class allowed it to pass the deserialisation with no errors; however – I was now facing a second problem – after indicating that my class implements ISerializable and addin the constructor required the members of the Exception class, from which my class inherited, including the Data member now deserialised correctly; my own class' member,however, did not.

There are two ways to overcome this - I could have simply mapped my properties (after all I only had a couple of strings to keep with the exception) to the Exception's Data property (have the getter and setter of each property use the collection internally, and so all the data will be capture in the Exception base class and so serialised with it, or - I could implement ISerializable fully which really only means

1. Firstly - adding my members to the serializationInfo member of GetObjectData:

public override void GetObjectData(SerializationInfo si, StreamingContext
context)
{
base.GetObjectData(si, context);
si.AddValue("member1Name", member1Value);
si.AddValue("member2Name", member2Value);
}

2. Secondly - populating the members back in the constructor:

protected MyCustomException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
member1Name= info.GetString("member1Value");
member2Name= info.GetString("member2Value");
}


Voila! it all serialises and deserialises ok now. if only I didn't have to spend a whole day to figure this out!

Labels: , ,

Sunday, February 11, 2007

AnonymousTypes and serialization

In my current project we have to deal a lot of serialization of deserialization of objects; mostly due to the fact that we have a common object model accross most of our services and that mostly it is these objects that gets passed between web services in our solution.

In fact, almost every object we have is represented by both a .net class and a BizTalk schema and for that reason we have decorated most of our classes with serilization attributes to control the format of the xml generated.

As you all know, when objects are used in exposed web services, a schema that represents them is generated and embedded in the WSDL file, from which proxy classes will be generated in consuming applications.

A while ago I’ve noticed that in most of our classes have within the xml serialization related attributes the following attribute –

System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)

Hvaing this instructs the the schema generator to avoid treating this class/type as a global type which prevents re-use.

To demonsrate the issue consider the following –

I have a web method HelloWorld that takes in 1 parameter myenum of type MyEnum (which is an enumeration)


If the enum type has the discussed dattribute the wsdl is generated as the following –




Notice that that enumeration is a local definition of the element.

Without the attribute the wsdl is generated like this –



Notice that now the enum is declared globally and can be re-used across the methods.

I'm not sure why we had this in, I suspect, if I'm not mistaken, that the xsd.exe tool adds this attribute to classes generated from xml, and we've used this in many cases as our template.
Either way - I now try to avoid this attribute generally and only apply it when it makes sense. otherwise I have all the types declared as non anonymous which enables reuse.

Labels: