Serialization is the process of encoding an object or class into a persistent or transportable state. This allows you to take a complex data type, then encode, save, transfer, and decode it, with the possibility that a separate process handles the decoding.
There are two main serialization types:
Binary serialization — Takes the data type and converts it into a binary stream.
XML serialization — Converts the data type into an XML stream which you can then convert to an XML document.
You can take the output from either serialization type and store it in memory, place it into a file, or pass it across a network connection. For example, your application may have a defined CustomerData complex data type which stores information about a customer (such as name, address, telephone number, and so on). You can use serialization to convert the CustomerData data type into a binary or XML stream that you can then transport across process boundaries or save to a file for later use. The object is serialized when it is in the binary or XML format.
De-serialization is the process of converting a serialized object back to its original form. Generally, you de-serialize objects back into their original type. Hence, if you serialize the CustomerData data type as a binary stream or as an XML document, you de-serialize it back to the data type CustomerData and not to the data type OrderData.
Both .NET and J2EE use serialization to exchange data between applications within the same platform. You can also use serialization to exchange data between applications on different platforms by passing serialized objects for de-serialization on the alternate platform. The next sections examine how to implement binary and XML serialization on .NET and Java.
Binary serialization is the process of taking a complex data type (or object) and encoding it into a binary stream, changing to a persistent state, transporting, and then decoding (de-serialize) back into the original complex data type.
Both Java and .NET include a binary serializer that can convert any serializeable data type into a byte stream. The classes that perform this serialization are similar in each platform and simple to implement.
For binary serialization in both .NET and Java, you must first apply a label to indicate that you want to serialize a type. In .NET, you can use the [Serializable] attribute or implement the ISerializable interface. In Java, the equivalent approach is to make the class implement java.io.serializable.
Unfortunately, the .NET and Java serializers are incompatible. Hence, you cannot stream the serialized version of the CustomerData object output from the Java serializer straight into the .NET version and vice versa. Even if you could, you would still face the challenge of getting the .NET Framework application to understand the CustomerData object that the Java serializer produced. The .NET side may not have an equivalent CustomerData data type to accept the de-serialized CustomerData object from the Java side.
You can use binary serialization for linking .NET to Java as long as the same formatter performs the serialization and de-serialization of an object. The format that creates the byte stream from the data type must match exactly the format that receives the byte stream and reconstructs the object.
There are two approaches that you can use to circumvent the incompatibility of the default J2EE and .NET binary serializers. These are:
Create a custom serializer sharing the same formatting options on both Java and .NET.
Use a third-party product that works with the binary formatter in the .NET Framework, such as Ja.NET or JNBridgePro.
For more information about implementing binary serialization with Ja.NET and JNBridgePro, see Chapter 4, “Interoperability Technologies: Point to Point.”
XML serialization is the process of taking a complex data type (or object) and encoding it into an XML stream. You can then make this XML stream into a persistent state in the form of an XML document, transport it, and then later decode (de-serialize) it back into the original complex data type (or object).
In order to understand the process of XML serialization, you need to have a basic understanding of XML. XML is text-based document markup language that contains structured and extensible data. XML is text-based, so you can read it like normal text and because it is extensible, you can use it to describe almost any type of information. Hence XML documents can contain:
XML documents may also contain instructions about how to use the data within the document itself.
For more information about XML, see the Microsoft XML Web site.
In the “Understanding Binary Serialization” section, you saw how the binary serializers of the .NET and Java platforms are not compatible with each other. However, XML is platform independent. If you can serialize an object or data type from one platform into an XML document, it should be relatively easy to read, understand, and de-serialize this document back into an object or data type on the other platform. Unfortunately, this is not always the case, but XML serialization does provide an interoperability route in most situations.
There are several different ways to read, write, and edit XML documents within .NET and J2EE. This process is XML parsing and both platforms have stable and mature XML parsers. Using a parser, you can write code within your application that reads data from an XML document manually and then inserts it into a complex data type object. For example, you could use parsing to read data from an XML document that a J2EE application generates, and then insert the data into a .NET data type. Parsing allows the exchange of data between .NET and J2EE.
Parsers for reading and writing XML documents tend to fall into three main types:
Document Object Model (DOM) on both platforms
Simple API for XML (SAX) on Java only
Pull model parsing on .NET only
DOM XML parsers load the entire document into memory, which has some advantages and disadvantages. With the whole document in memory, you can traverse the XML hierarchy quickly and easily, but large documents affect performance and responsiveness due to memory usage.
SAX reads sections of the XML file as required. This affects performance less due to reading the file on demand, but it reduces flexibility by preventing backwards parsing.
Pull model parsing uses a forward-only, read-only XmlReader cursor. XMLReader provides fast, non-cached stream access to the input data, allowing you to pull data and to skip records that are of no interest. Because XmlReader is pull mode, applications can pull nodes from the reader as necessary. The pull model provides advantages such as state management, multiple input streams, extra string copy avoidance, and selective processing. For more information about using XmlReader, see “Reading XML with the XmlReader,” on MSDN.
The System.Xml namespace provides the XmlDocument and XmlElement classes to allow you to parse XML in .NET. They also provide methods that let you add to and modify elements within XML documents and to traverse those documents.
In Java, you can use Document and Element classes to achieve similar results.
There are some limitations to XML parsing that you need to understand. Parsing works well when you access and read distinct data elements from an XML document. However, manipulating information within the document itself can rapidly become unwieldy. Also, there is no intrinsic way of using a parser to map objects in the XML document to classes either in Java or in .NET. Mapping data in an XML document to application objects and classes requires XML serialization.
XML parsing can be considered an inefficient way of implementing XML serialization and is not the recommended way for exchanging XML data between .NET and Java. Both .NET and J2EE have XML serializers that include parsing functionality.
XML serialization provides significant enhancements when creating XML documents. It frees you from the tedium of the XML parsing process, letting you concentrate on developing the application itself and associated data types. XML serialization simplifies the task of taking an object in .NET, converting it to an XML document and enabling a Java application to read it, and vice versa.
The.NET Framework provides numerous classes and APIs for XML serialization. The main one of these is the System.Xml.Serialization.XmlSerializer class. This class allows the serialization of .NET Framework types to an XML document and back again. XML serialization converts (serializes) only the public fields and properties of an object into an XML stream whereas binary serialization converts the object’s public and private fields and the name of the class together with the assembly containing the class into a stream of bytes.
You can use the [Serializable] attribute to indicate that you want to serialize a type, just like with binary serialization. Alternatively, you can have your data type class implement the ISerializable interface.
XML serialization uses different rules from binary serialization. For example, you do not need to label .NET data types with the [Serializable] attribute, although this is still recommended practice. However, .NET data types must have a valid default public constructor.
The following example demonstrates how you can use XMLSerializer in .NET to convert an instance of the CustomerData class, called custData, into an XML stream. This example assumes your .NET Framework application already has a defined CustomerData data type class which you marked as serializable.
XmlSerializer serializer = new XmlSerializer(typeof(CustomerData)); serializer.Serialize(fileStream, custData);
The Serialize method of the XMLSerializer class serializes the contents of the CustomerData object, custData, and generates the output as a file stream. This stream points to a file, which is an XML document representing the serialized version of the CustomerData object, custData.
This next example demonstrates how you can use the XMLSerializer in .NET to return an instance of a CustomerData object, custData, from an XML document stored as a file.
XmlSerializer serializer = new XmlSerializer(typeof(CustomerData)); CustomerData custData = (CustomerData)serializer.Deserialize(fileStream);
Here you use the Deserialize method of the XmlSerializer class to return a CustomerData object, custData, from an XML document passed to the serializer as a stream.
The Deserialize method returns an object with a type of System.Object. Ensure that you cast the returned object appropriately before assigning it to the target type.
Serialization can fail for a number of reasons:
The XML document under de-serialization contains fields the serializer cannot process.
The object cannot be serialized, such as any object that implements the IDictionary interface.
Objects are not declared as public.
Objects do not contain a valid no-argument constructor.
Attempting to serialize an object that fits into one of the previous four categories generates a System.InvalidOperationException from XmlSerializer. When debugging XML serialization, you should check for and resolve any instances of this exception.
XML serialization of objects is not part of the core specifications for either J2SE or J2EE. Hence, you need to use a third party serializer, of which there are several to choose from:
Open XML Framework (OXF)
Java Architecture for XML Binding (JAXB)
Many serializers form part of a larger Web services package, although you do not need to be using Web services to use the XML serialization features. Java serializers use a combination of reflection and mapping to write the object’s data into the correct XML format. Reflection is a technique for inspecting the structure of an object, and mapping is like an Extensible Stylesheet Language Transformations (XSLT) document, mapping the fieldnames in the object to elements or attributes in the XML document.
To make a Java class serializable, you need to implement the Serializable interface. Hence the class declaration for CustomerData would be the following.
public class CustomerData implements java.io.Serializable
A serializable class must also have a no argument constructor. You can also use the serialPersistentFields member to declare explicitly which fields can be serialized, or the transient keyword to indicate fields that cannot be serialized. Examples of non serializable fields might be ones that include sensitive data.
Classes that you have marked for serialization in Java may also implement the readObject and writeObject methods for controlling saving, and reading information, together with the writeReplace and readResove methods to designate replacement objects.
You may also implement Externalizable instead of Serializable for complete control of class serialization. When using Externalizable, you have to implement the writeExternal and readExternal methods to write, not only the contents of the data, but also the data format and associated metadata.
The next two examples use Electric XML to demonstrate how to serialize and deserialize a Java object. Here you see that Electric XML handles the serialization and deserialization processes in a similar fashion to .NET.
The following example assumes that your Java application defines a serializable OrderData data type class which implements java.io.serializable. It demonstrates how you can use the Electric XML serializer in J2EE to convert a named instance of this class, order, into an XML document.
electric.xml.io.IWriter writer = new LiteralWriter(xmlNameSpace,OrderData); writer.writeObject(order); electric.xml.Document document = writer.getDocument();
In this example, you first create an instance of type electric.xml.io.IWriter, passing in the XML namespace of the XML document you want to create, and the object type (OrderData) as parameters. The writeObject method of the IWriter object serializes the contents of the OrderData object, order. Finally, the getDocument method of the IWriter object retrieves the XML document containing the serialized contents of the OrderData object. You can then write this document to a stream.
This next example demonstrates how you can use the Electric XML serializer in J2EE to return an instance of an OrderData object, order, from an XML document named xmlDocument.
electric.xml.io.IReader reader = new LiteralReader(xmlDocument); OrderData order = (OrderData)reader.readObject(OrderData.class);
Here you use the readObject method of the IReader class to return an OrderData object, order, from an XML document which you pass to the reader as a parameter.
The readObject method returns a generic object of type java.lang.Object. You must cast this returned object appropriately before assigning it to the target type.
For an additional example of XML Serialization on the Java platform, see Chapter 7, “Integrating .NET in the Presentation Tier.”