Using the VMware GemFire® PDX Autoserializer

It’s extremely easy for .NET applications to store and retrieve data from Geode. All that’s required is a single line of code to set the PdxSerializer to the .NET client’s ReflectionBasedAutoSerializer:

cache.TypeRegistry.PdxSerializer = new ReflectionBasedAutoSerializer();

(The autoserializer can be registered only in the application code. It cannot be configured declaratively in cache.xml.)

After an autoserializer has been registered, all user classes can automatically be stored without needing to implement any interfaces. For example, the following put command stores value, which is an instance of a user defined class, in the region.

region.put(key, value);

The .NET client’s ReflectionBasedAutoSerializer supports the full list of .NET primitives and other common built-in types.

Types without a no-arg constructor are not supported by the ReflectionBasedAutoSerializer. This includes all user defined structs. System.Data.Datatable is an example of a system class that is not supported due to lack of a no-arg constructor.

Java Interoperability with .NET Specific Types

Java does not have unsigned data types or exact equivalents of Guid and Decimal. Care should be taken when passing these types between .NET and Java applications using the .NET ReflectionBasedAutoSerializer. For example, if storing UInt16 data (that is, 16-bit unsigned values), be aware that values greater than UInt16.MaxValue/2 - 1 will show up as negative numbers in Java. Using that data in a Java application may have unexpected behavior. If you expect to exceed half the range for the given type (Byte.MaxValue/2, UInt16.MaxValue/2, UInt32.MaxValue/2, or UInt64.MaxValue/2) you should use the next larger type. This obviously breaks down for UInt64, which has no next larger type. However, if your range exceeds UIn64.MaxValue/2, you likely have a much more complex set of issues to deal with, such as heavy paging due to such a large data set.

Remote Queries of .NET Only Types

At this time the .NET Client does not support queries against user classes that have been stored on the server using the ReflectionBasedAutoSerializer.

When you register the reflection-based serializer, VMware GemFire® uses it to serialize all objects that do not implement IPdxSerializable. You can customize the auto-serialization behavior for your domain objects by adding serialization attributes to your object’s fields.

Extending the PDX Autoserializer

For each object you intend to have autoserialized, you can customize the serialization as needed.

Note: If you also use PDX serialization in Java for the object, customize your serialization in the same way for both languages.

The following extension methods apply to autoserialization:

  • WriteTransform. Controls what field value is written during auto serialization.
  • ReadTransform. Controls what field value is read during auto deserialization.
  • GetFieldType. Defines the specific field names that will be generated during autoserialization.
  • IsIdentityField. Controls which field is marked as the identity field. Identity fields are used when a PdxInstance computes its hash code to determine whether it is equal to another object.
  • GetFieldType. Determines the field type that will be used when autoserializing the given field.
  • IsFieldIncluded. Specifies which fields of a class to autoserialize.

To specify an identifier field in your domain object, add the attribute PdxIdentityField to the field.

For example:

[PdxIdentityField] private int id;

To exclude a field from serialization, add the .NET attribute NonSerialized to the field.

For example:

[NonSerialized] private int myLocalData;

For each domain class VMware GemFire® serializes using the autoserializer, all fields are considered for serialization except those defined as static, literal or readonly and those you explicitly exclude using the .NET NonSerialized attribute.

This example code demonstrates how to extend the autoserializer to customize serialization.

public class AutoSerializerEx : ReflectionBasedAutoSerializer
{
   public override object WriteTransform(FieldInfo fi, Type type, object originalValue) {
      if (fi.FieldType.Equals(Type.GetType("System.Guid"))) {
        return originalValue.ToString();
      } else if (fi.FieldType.Equals(Type.GetType("System.Decimal"))) {
        return originalValue.ToString();
      } else
        return base.WriteTransform(fi, type, originalValue);
    }

    public override object ReadTransform(FieldInfo fi, Type type, object serializeValue) {
      if (fi.FieldType.Equals(Type.GetType("System.Guid"))) {
        Guid g = new Guid((string)serializeValue);
        return g;
      } else if (fi.FieldType.Equals(Type.GetType("System.Decimal"))) {
        return Convert.ToDecimal((string)serializeValue);
      } else
        return base.ReadTransform(fi, type, serializeValue);
    }

   public override FieldType GetFieldType(FieldInfo fi, Type type) {
      if (fi.FieldType.Equals(Type.GetType("System.Guid")) ||
              fi.FieldType.Equals(Type.GetType("System.Decimal")))
        return FieldType.STRING;
      return base.GetFieldType(fi, type);
   }

   public override bool IsIdentityField(FieldInfo fi, Type type) {
      if (fi.Name == "_identityField")
        return true;
      return base.IsIdentityField(fi, type);
   }

   public override string GetFieldName(FieldInfo fi, Type type) {
      if (fi.Name == "_nameChange")
        return fi.Name + "NewName";
      return fi.Name;
    }

   public override bool IsFieldIncluded(FieldInfo fi, Type type)
   {
      if (fi.Name == "_notInclude")
        return false;
      return base.IsFieldIncluded(fi, type);
    }
}