Document 'pdxSerializable' example (#496)

* GEODE-6545: Document 'pdxSerializable' example
diff --git a/docs/geode-native-docs/serialization/data-serialization.html.md.erb b/docs/geode-native-docs/serialization/data-serialization.html.md.erb
index 6ed5a60..41643f7 100644
--- a/docs/geode-native-docs/serialization/data-serialization.html.md.erb
+++ b/docs/geode-native-docs/serialization/data-serialization.html.md.erb
@@ -27,3 +27,277 @@
 
 To learn more about other serialization options, see the [Data Serialization section in the _<%=vars.product_name_long%> User Guide_](serverman/developing/data_serialization/chapter_overview.html).
 
+<a id="implementing_pdxsbl_class"></a>
+## Implementing a PdxSerializable Custom Class
+
+PdxSerializable provides custom serialization to an individual class. Fields within an object can be serialized separately from the rest of the class.
+
+The type of serialization and its implementation can be specified entirely in the client
+application, with no need to create corresponding code on the <%=product_name%> server.
+
+<a id="setup_pdxsbl_class"></a>
+### Setup
+
+PdxSerializable provides custom serialization to an individual class. Fields within an object can be serialized separately from the rest of the class.
+
+Setting up a custom class for PdxSerializable treatment requires some preparatory steps:
+
+- The custom class must inherit from the .NET IPdxSerializable interface or the C++ PdxSerializable interface.
+
+- You must provide serialization instructions for objects of the custom class. Specifically:
+
+  -  You must implement the `ToData()` and `FromData()` methods for .NET applications, or the `toData()` and
+  `fromData()` methods in C++ applications.
+  -  You must provide a "factory method" that returns an instance of the custom object.
+
+- Your application must register your custom class with the cache, which takes care of informing the server of your
+  serialization scheme. With registration, you provide the name of the "factory method" you created for instantiating
+  objects of the custom class.
+
+<a id="pdxsbl_examples"></a>
+## PdxSerializable Examples
+
+The native client release contains examples, written for .NET and C++, showing how a client application
+can register for serialization of custom objects using the .NET IPdxSerializable interface or the C++ PdxSerializable interface.
+
+The examples are located in `examples\dotnet\pdxserializable` and `examples/cpp/pdxserializable`, respectively. 
+
+The examples define the serializable class, `Orders`, including its serialization and deserialization methods and its factory method.
+Once these pieces are in place, execution is simple: the main routine of the example registers the serializable class then performs some put and get operations.
+
+<a id="pdxsbl_execution"></a>
+### Execution
+
+Both examples perform the same sequence of operations, displaying simple log entries as they run.
+
+- To run an example, follow the instructions in the README.md file in the example directory.
+- Review the source code in the example directory to see exactly how it operates.
+
+- Begin by running a script that sets up the server-side environment by invoking `gfsh` commands to create a region, a locator, and a server.
+
+- Run the example client application, which performs the following steps:
+
+  - Connects to the server
+  - Registers the PdxSerializable class
+  - Creates orders
+  - Stores orders
+  - Retrieves orders
+
+
+<a id="pdsxbl_dotnet_example"></a>
+### .NET Example
+
+This section contains code snippets showing highlights of the .NET PdxSerializable example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The .NET example defines a PdxSerializable class called `Order` that inherits from the `IPdxSerializable` interface.
+An `Order` object contains three fields:
+
+- an integer `order_id`
+- a string `name`
+- a short-int `quantity`
+
+From Order.cs:
+
+```csharp
+  public class Order : IPdxSerializable
+  {
+    ...
+    public long OrderId { get; set; }
+    public string Name { get; set; }
+    public short Quantity { get; set; }
+```
+
+Using the `IPdxSerializable` read and write methods, the `Order` class defines `ToData()` and `FromData()`
+methods that perform the serialization and deserialization operations, respectively, and the `CreateDeserializable()` factory method:
+
+From Order.cs:
+
+```csharp
+    public void ToData(IPdxWriter output)
+    {
+      output.WriteLong(ORDER_ID_KEY_, OrderId);
+      output.MarkIdentityField(ORDER_ID_KEY_);
+
+      output.WriteString(NAME_KEY_, Name);
+      output.MarkIdentityField(NAME_KEY_);
+
+      output.WriteInt(QUANTITY_KEY_, Quantity);
+      output.MarkIdentityField(QUANTITY_KEY_);
+    }
+
+    public void FromData(IPdxReader input)
+    {
+      OrderId = input.ReadLong(ORDER_ID_KEY_);
+      Name = input.ReadString(NAME_KEY_);
+      Quantity = (short)input.ReadInt(QUANTITY_KEY_);
+    }
+
+    public static IPdxSerializable CreateDeserializable()
+    {
+      return new Order();
+    }
+```
+
+The .NET example mainline creates a cache, then uses it to register the PdxSerializable class that was created in Orders.cs:
+
+```csharp
+   var cacheFactory = new CacheFactory()
+       .Set("log-level", "none");
+   var cache = cacheFactory.Create();
+
+   cache.TypeRegistry.RegisterPdxType(Order.CreateDeserializable);
+```
+
+The client creates a connection pool and a region named "example_orderobject":
+
+```csharp
+   var poolFactory = cache.GetPoolFactory()
+       .AddLocator("localhost", 10334);
+   poolFactory.Create("pool");
+
+   var regionFactory = cache.CreateRegionFactory(RegionShortcut.PROXY)
+        .SetPoolName("pool");
+   var orderRegion = regionFactory.Create<int, Order>("example_orderobject");
+```
+
+After declaring some keys and values, the client then stores and retrieves an `Order` object:
+
+```csharp
+    const int orderKey = 65;
+
+    var order = new Order(orderKey, "Donuts", 12);
+
+    Console.WriteLine("order to put is " + order);
+    orderRegion.Put(orderKey, order, null);
+
+    Console.WriteLine("Successfully put order, getting now...");
+    var orderRetrieved = orderRegion.Get(orderKey, null);
+
+    Console.WriteLine("Order key: " + orderKey + " = " + orderRetrieved);
+```
+
+Finally, the application closes the cache:
+
+```csharp
+    cache.Close();
+```
+
+<a id="pdxsbl_cpp_example"></a>
+### C++ Example
+
+This section contains code snippets showing highlights of the C++ PdxSerialiable example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The C++ example defines a PdxSerializable class called `Order` that inherits from the `PdxSerializable` interface.
+An `Order` object contains three fields:
+
+- an integer `order_id`
+- a string `name`
+- a short-int `quantity`
+
+From Order.hpp:
+
+```cpp
+class Order : public PdxSerializable {
+ public:
+ ...
+
+ private:
+  int32_t order_id_;
+  std::string name_;
+  int16_t quantity_;
+};
+
+```
+
+Using the PdxSerializable read and write methods, the `Order` class defines `fromData()` and `toData()`
+methods that perform the deserialization and serialization operations, respectively, and the
+`createDeserializable()` factory method:
+
+From Order.cpp:
+
+```cpp
+void Order::fromData(PdxReader& pdxReader) {
+  order_id_ = pdxReader.readInt(ORDER_ID_KEY_);
+  name_ = pdxReader.readString(NAME_KEY_);
+  quantity_ = pdxReader.readShort(QUANTITY_KEY_);
+}
+
+void Order::toData(PdxWriter& pdxWriter) const {
+  pdxWriter.writeInt(ORDER_ID_KEY_, order_id_);
+  pdxWriter.markIdentityField(ORDER_ID_KEY_);
+
+  pdxWriter.writeString(NAME_KEY_, name_);
+  pdxWriter.markIdentityField(NAME_KEY_);
+
+  pdxWriter.writeShort(QUANTITY_KEY_, quantity_);
+  pdxWriter.markIdentityField(QUANTITY_KEY_);
+}
+
+...
+
+std::shared_ptr<PdxSerializable> Order::createDeserializable() {
+  return std::make_shared<Order>();
+}
+```
+
+The C++ example mainline creates a cache, then uses it to create a connection pool and a region object (of class `Region`).
+
+```cpp
+  auto cacheFactory = CacheFactory();
+  cacheFactory.set("log-level", "none");
+  auto cache = cacheFactory.create();
+  auto poolFactory = cache.getPoolManager().createFactory();
+
+  poolFactory.addLocator("localhost", 10334);
+  auto pool = poolFactory.create("pool");
+  auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY);
+  auto region = regionFactory.setPoolName("pool").create("custom_orders");
+```
+
+The client registers the PdxSerializable class that was created in Orders.cpp:
+
+```cpp
+  cache.getTypeRegistry().registerPdxType(Order::createDeserializable);
+```
+
+The client then instantiates and stores two `Order` objects:
+
+```cpp
+  auto order1 = std::make_shared<Order>(1, "product x", 23);
+  auto order2 = std::make_shared<Order>(2, "product y", 37);
+
+  region->put("Customer1", order1);
+  region->put("Customer2", order2);
+```
+
+Next, the application retrieves the stored values, in one case extracting the fields defined in
+the serialization code:
+
+```cpp
+  if (auto order1retrieved =
+          std::dynamic_pointer_cast<Order>(region->get("Customer1"))) {
+    std::cout << "OrderID: " << order1retrieved->getOrderId() << std::endl;
+    std::cout << "Product Name: " << order1retrieved->getName() << std::endl;
+    std::cout << "Quantity: " << order1retrieved->getQuantity() << std::endl;
+  } else {
+    std::cout << "Order 1 not found." << std::endl;
+  }
+```
+
+The application retrieves the second object and displays it without extracting the separate fields:
+
+```cpp
+  if (region->existsValue("rtimmons")) {
+    std::cout << "rtimmons's info not deleted" << std::endl;
+  } else {
+    std::cout << "rtimmons's info successfully deleted" << std::endl;
+  }
+```
+
+Finally, the application closes the cache:
+
+```cpp
+  cache.close();
+```