[TOC] [Prev] [Next]

# Object Serialization Stream Protocol

### Topics:

- Overview
- Stream Elements
- Grammar for the Stream Format
- Example

# Overview

The stream format is designed to satisfy the following goals:

- Be compact and structured for efficient reading.
- Allow skipping through the stream using only the knowledge of the structure and format of the stream. Do not require any per class code to be invoked.
- Require only stream access to the data.

# Stream Elements

A basic structure is needed to represent objects in a stream. Each attribute of the object needs to be represented: its classes, its fields, and data written and later read by class-specific methods. The representation of objects in the stream can be described with a grammar. There are special representations for null objects, new objects, classes, arrays, strings, and back references to any object already in the stream. Each object written to the stream is assigned a handle that is used to refer back to the object. Handles are assigned sequentially starting from zero. The handles restart at zero when the stream is reset.

A class object is represented by:

- Its ObjectStreamClass object.

An ObjectStreamClass object is represented by:

- The Stream Unique Identifier (SUID) of compatible classes.
- A flag indicating if the class had writeObject/readObject methods.
- The number of nonstatic and nontransient fields.
- The array of fields of the class that are serialized by the default mechanism. For arrays and object fields, the type of the field is included as a string.
- Optional block-data records or objects written by the annotateClass method.
- The ObjectStreamClass of its supertype (null if the superclass is not serializable).

Strings are represented by their UTF encoding. Note, the current specification and implementation of the modified UTF is restricts the total length of the encoded string to 65535 characters.

Arrays are represented by:

- Their ObjectStreamClass object.
- The number of elements.
- The sequence of values. The type of the values is implicit in the type of the array. for example the values of a byte array are of type byte.

New objects in the stream are represented by:

- The most derived class of the object.
- Data for each serializable class of the object, with the highest superclass first. For each class the stream contains:
  - The default serialized fields (those fields not marked static or transient as described in the corresponding ObjectStreamClass).
  - If the class has `writeObject`/`readObject` methods, there may be optional objects and/or block-data records of primitive types written by the `writeObject` method followed by an endBlockData code.

All primitive data written by classes is buffered and wrapped in block-data records whether the data is written to the stream within a `writeObject` method or written directly to the stream from outside a `writeObject` method. This data may only be read by the corresponding `readObject` methods or directly from the stream. Objects written by `writeObject` terminate any previous block-data record and are written as regular objects, or null or back references as appropriate. The block-data records allow error recovery to discard any optional data. When called from within a class, the stream can discard any data or objects until the endBlockData.

# Grammar for the Stream Format

The table below contains the grammar. Nonterminal symbols are shown in italics. Terminal symbols in a fixed width font. Definitions of nonterminals are followed by a ":". The definition is followed by one or more alternatives, each on a separate line. The following table describes the notation:

Notation | Meaning
--- | ---
(datatype) | This token has the data type specified, such as byte.
token[n] | A predefined number of occurrences of the token, that is an array.
x0001 | A literal value expressed in hexadecimal. The number of hex digits reflects the size of the value.
<xxx> | A value read from the stream used to indicate the length of an array.

## Rules of the Grammar

A Serialized stream is represented by any stream satisfying the stream rule.

    stream:
    	magic version contents

    contents:
    	content
    	contents content

    content:
    	object
    	blockdata

    object:
    	newObject
    	newClass
    	newArray
    	newString
    	newClassDesc
    	prevObject
    	nullReference
    	exception
    	TC_RESET

    newClass:
    	TC_CLASS classDesc newHandle

    classDesc:
    	newClassDesc
    	nullReference
    	(ClassDesc)prevObject	// an object required to be of type ClassDesc

    superClassDesc:
    	classDesc

    newClassDesc:
    	TC_CLASSDESC className serialVersionUID	newHandle classDescInfo

    classDescInfo:
    	classDescFlags fields classAnnotation superClassDesc

    className:
    	(utf)

    serialVersionUID:
    	(long)

    classDescFlags:
    	(byte)	// Defined in Terminal Symbols and Constants

    fields:
    	(short)<count>  fieldDesc[count]

    fieldDesc:
    	primitiveDesc
    	objectDesc

    primitiveDesc:
    	prim_typecode fieldName

    objectDesc:
    	obj_typecode fieldName className

    fieldName:
    	(utf)

    className:
    	(String)object		// String containing the field's type

    classAnnotation:
    	endBlockData
    	contents endBlockData	// contents written by annotateClass

    prim_typecode:
    	`B' 	// byte
    	`C' 	// char
    	`D' 	// double
    	`F' 	// float
    	`I' 	// integer
    	`J' 	// long
    	`S' 	// short
    	`Z' 	// boolean

    obj_typecode:
    	`[`	// array
    	`L' 	// object

    newArray:
    	TC_ARRAY classDesc newHandle (int)<size> values[size]

    newObject:
    	TC_OBJECT classDesc newHandle classdata[]	// data for each class

    classdata:
    	nowrclass	// SC_WRRD_METHOD & !classDescFlags
    	wrclass objectAnnotation	// SC_WRRD_METHOD & classDescFlags

    nowrclass:
    	values	// fields in order of class descriptor

    wrclass:
    	nowrclass

    objectAnnotation:
    	endBlockData
    	contents endBlockData	// contents written by writeObject

    blockdata:
    	TC_BLOCKDATA (byte)<size> (byte)[size]

    blockdatalong:
    	TC_BLOCKDATALONG (int)<size> (byte)[size]

    endBlockData	:
    	TC_ENDBLOCKDATA

    newString:
    	TC_STRING newHandle (utf)

    prevObject	:
    	TC_REFERENCE (int)handle

    nullReference	:
    	TC_NULL

    exception:
    	TC_EXCEPTION reset (Throwable)object	 reset

    resetContext:
    	TC_RESET

    magic:
    	STREAM_MAGIC

    version	:
    	STREAM_VERSION

    values:		// The size and types are described by the
    		// classDesc for the current object

    newHandle:		// The next number in sequence is assigned
    			// to the object being serialized or deserialized

## Terminal Symbols and Constants

The following symbols in java.io.ObjectStreamConstants define the terminal and constant values expected in a stream.

    	final static short STREAM_MAGIC = (short)0xaced;
    	final static short STREAM_VERSION = 5;
    	final static byte TC_NULL = (byte)0x70;
    	final static byte TC_REFERENCE = (byte)0x71;
    	final static byte TC_CLASSDESC = (byte)0x72;
    	final static byte TC_OBJECT = (byte)0x73;
    	final static byte TC_STRING = (byte)0x74;
    	final static byte TC_ARRAY = (byte)0x75;
    	final static byte TC_CLASS = (byte)0x76;
    	final static byte TC_BLOCKDATA = (byte)0x77;
    	final static byte TC_ENDBLOCKDATA = (byte)0x78;
    	final static byte TC_RESET = (byte)0x79;
    	final static byte TC_BLOCKDATALONG = (byte)0x7A;
    	final static byte TC_EXCEPTION = (byte)0x7B;

The flag byte classDescFlags may include values of

    	final static byte SC_WRITE_METHOD = 0x01;
    	final static byte SC_SERIALIZABLE = 0x02;
    	final static byte SC_EXTERNALIZABLE = 0x04;

The flag SC_WRITE_METHOD is set if the class writing the stream had a writeObject method that may have written additional data to the stream. In this case a TC_ENDBLOCKDATA marker is always expected to terminate the data for that class.

The flag SC_SERIALIZABLE is set if the class that wrote the stream extended `java.io.Serializable` but not java.io.Externalizable, the class reading the stream must also extend Serializable and the default serialization mechanism is to be used.

The flag SC_EXTERNALIZABLE is set if the class that wrote the stream extended `java.io.Externalizable`, the class reading the data must also extend Externalizable and the data will be read using it's writeExternal and readExternal methods.

# Example

Consider the case of an original class and two instances in a linked list:

    class List implements java.io.Serializable {
    	int value;
    	List next;
    	public static void main(String[] args) {
    		try {
    			List list1 = new List();
    			List list2 = new List();
    			list1.value = 17;
    			list1.next = list2;
    			list2.value = 19;
    			list2.next = null;

    			ByteArrayOutputStream o = new ByteArrayOutputStream();
    			ObjectOutputStream out = new ObjectOutputStream(o);
    			out.writeObject(list1);
    			out.writeObject(list2);
    			out.flush();
    			...
    		} catch (Exception ex) {
    			ex.printStackTrace();
    		}
    	}
    }

The resulting stream contains:

    00: ac ed 00 05 73 72 00 04 4c 69 73 74 69 c8 8a 15 >....sr..Listi...<

    10: 40 16 ae 68 02 00 02 49 00 05 76 61 6c 75 65 4c >Z......I..valueL<

    20: 00 04 6e 65 78 74 74 00 06 4c 4c 69 73 74 3b 78 >..nextt..LList;x<

    30: 70 00 00 00 11 73 71 00 7e 00 00 00 00 00 13 70 >p....sq.~......p<

    40: 71 00 7e 00 03                                  >q.~..<

[TOC] [Prev] [Next]

Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved.