diff --git a/helios/datatypes/2011/01.py b/helios/datatypes/2011/01.py
index a2edc83c6f75aaa00bc73f190fe68d30c5ef01d4..ca5647947782c23536c45eff5f4d43ba58641d04 100644
--- a/helios/datatypes/2011/01.py
+++ b/helios/datatypes/2011/01.py
@@ -4,5 +4,18 @@ data types for 2011/01 Helios
 
 from helios.datatypes import LDObject
 
+class Trustee(LDObject):
+  """
+  a trustee
+  """
+
+  FIELDS = ['uuid', 'public_key', 'public_key_hash', 'pok', 'decryption_factors', 'decryption_proofs', 'email']
+  STRUCTURED_FIELDS = {
+      'pok' : 'pkc/elgamal/DiscreteLogProof',
+      'public_key' : 'pkc/elgamal/PublicKey'
+      }
+
+  # removed some public key processing for now
+ 
 class Election(LDObject):
     pass
diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py
index d336b00cde8632a7d7ef4279cdbe8cc908edc32e..ef8ed017ec5c5cfb892d0054d74975b5c52421e1 100644
--- a/helios/datatypes/__init__.py
+++ b/helios/datatypes/__init__.py
@@ -34,7 +34,8 @@ class LDObject(object):
     data format. For example, a legacy election LDObject instance will wrap an Election object
     and serialize its fields according to the specs for that version.
 
-    To accomodate old JSON types, we allow  classes to override default JSON-LD fields.
+    To accomodate old JSON types, we allow  classes to do basically whatever they want,
+    or to let this base class serialize pure JSON thingies, without the JSON-LD.
     """
 
     # whether or not to add JSON-LD things
@@ -43,26 +44,56 @@ class LDObject(object):
     # fields to serialize
     FIELDS = []
 
+    # structured fields
+    STRUCTURED_FIELDS = {}
+
+    def __init__(self, wrapped_obj):
+        self.wrapped_obj = wrapped_obj
+        self.structured_fields = {}
+
     @classmethod
-    def instantiate(cls, obj):
-        if not hasattr(obj, 'datatype'):
+    def get_class(cls, datatype):
+        # parse datatype string "v31/Election" --> from v31 import Election
+        parsed_datatype = datatype.split("/")
+
+        # get the module
+        dynamic_module = __import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1)
+
+        # go down the attributes to get to the class
+        dynamic_ptr = dynamic_module
+        for attr in parsed_datatype[1:]:
+            dynamic_ptr = getattr(dynamic_ptr, attr)
+        dynamic_cls = dynamic_ptr
+        
+        return dynamic_cls
+        
+    @classmethod
+    def instantiate(cls, obj, datatype=None):
+        if hasattr(obj, 'datatype') and not datatype:
+            datatype = getattr(obj, 'datatype')
+
+        if not datatype:
             raise Exception("no datatype found")
 
-        # parse datatype string "v31/Election" --> from v31 import Election
-        parsed_datatype = obj.datatype.split("/")
+        # the class
+        dynamic_cls = cls.get_class(datatype)
+
+        # instantiate it
+        return_obj = dynamic_cls(obj)
 
-        # construct it
-        dynamic_cls = getattr(__import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1), parsed_datatype[len(parsed_datatype)-1])
+        # go through the subfields and instantiate them too
+        for subfield_name, subfield_type in dynamic_cls.STRUCTURED_FIELDS.iteritems():
+            return_obj.structured_fields[subfield_name] = cls.instantiate(getattr(return_obj.wrapped_obj, subfield_name), datatype = subfield_type)
 
-        return dynamic_cls(obj)
+        return return_obj
 
     def set_from_args(self, **kwargs):
         for f in self.FIELDS:
             if kwargs.has_key(f):
                 new_val = self.process_value_in(f, kwargs[f])
-                setattr(self, f, new_val)
+                setattr(self.wrapped_obj, f, new_val)
             else:
-                setattr(self, f, None)
+                setattr(self.wrapped_obj, f, None)
         
     def serialize(self):
         return utils.to_json(self.toDict())
@@ -70,17 +101,23 @@ class LDObject(object):
     def toDict(self, alternate_fields=None):
         val = {}
         for f in (alternate_fields or self.FIELDS):
-            val[f] = self.process_value_out(f, getattr(self, f))
+            # is it a structured subfield?
+            if self.structured_fields.has_key(f):
+                val[f] = self.structured_fields[f].toDict()
+            else:
+                val[f] = self.process_value_out(f, getattr(self.wrapped_obj, f))
         return val
     
     @classmethod
     def fromDict(cls, d):
+        raise Exception("not a good idea yet")
+
         # go through the keys and fix them
         new_d = {}
         for k in d.keys():
             new_d[str(k)] = d[k]
       
-        return cls(**new_d)    
+        return cls(**new_d)
 
     @property
     def hash(self):
@@ -125,3 +162,25 @@ class LDObject(object):
     
         return other != None and self.uuid == other.uuid
   
+
+class ArrayOfObjects(LDObject):
+    """
+    If one type has, as a subtype, an array of things, then this is the structured field used
+    """
+
+    def __init__(self, wrapped_array, item_type):
+        self.item_type = item_type
+        self.items = [LDObject.instantiate(wrapped_item, item_type) for wrapped_item in wrapped_array]
+    
+    def toDict(self):
+        return [item.serialize() for item in self.items]
+
+def arrayOf(item_type):
+    """
+    a wrapper for the construtor of the array
+    returns the constructor
+    """
+    def array_constructor(wrapped_array):
+        return ArrayOfObjects(wrapped_array, item_type)
+
+    return array_constructor
diff --git a/helios/datatypes/core.py b/helios/datatypes/core.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0d488c9f2ddb3586217287a641005a3527c3b32
--- /dev/null
+++ b/helios/datatypes/core.py
@@ -0,0 +1,15 @@
+"""
+core data types
+"""
+
+from helios.datatypes import LDObject
+
+class BigInteger(LDObject):
+    """
+    A big integer is an integer serialized as a string.
+    We may want to b64 encode here soon.    
+    """
+
+    def toDict(self):
+        return str(self.wrapped_obj)
+
diff --git a/helios/datatypes/legacy.py b/helios/datatypes/legacy.py
index 941f15ff1d1003aed5eb6bf1189aaacc05cc01b6..1a10de2fba32425ef10f152df099868b11c8efd5 100644
--- a/helios/datatypes/legacy.py
+++ b/helios/datatypes/legacy.py
@@ -2,4 +2,34 @@
 Legacy datatypes for Helios (v3.0)
 """
 
-class 
+from helios.datatypes import LDObject, arrayOf
+
+class LegacyObject(LDObject):
+    USE_JSON_LD = False
+
+class Election(LegacyObject):
+    FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg',
+              'frozen_at', 'public_key', 'cast_url', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at']
+
+    STRUCTURED_FIELDS = {
+        'public_key' : 'legacy/EGPublicKey'
+        }
+        
+class EncryptedAnswer(LegacyObject):
+    FIELDS = ['choices', 'individual_proofs', 'overall_proof', 'randomness', 'answer']
+    STRUCTURED_FIELDS = {
+        'choices': arrayOf('pkc/elgamal/Ciphertext'),
+        'individual_proofs': arrayOf('pkc/elgamal/DisjunctiveProof'),
+        'overall_proof' : 'pkc/elgamal/DisjunctiveProof',
+        'randomness' : 'core/BigInteger'
+        # answer is not a structured field, it's an as-is integer
+        }
+
+class Voter(LegacyObject):
+    pass
+
+class CastVote(LegacyObject):
+    pass
+
+class Trustee(LegacyObject):
+    pass
diff --git a/helios/datatypes/pkc/__init__.py b/helios/datatypes/pkc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4783a0818c27153aa2fa36f252710c81fed43880
--- /dev/null
+++ b/helios/datatypes/pkc/__init__.py
@@ -0,0 +1,3 @@
+"""
+Public Key Cryptography datatypes
+"""
diff --git a/helios/datatypes/pkc/elgamal.py b/helios/datatypes/pkc/elgamal.py
new file mode 100644
index 0000000000000000000000000000000000000000..24729e6399904a9536b96bc61a290e43e9ef2a3a
--- /dev/null
+++ b/helios/datatypes/pkc/elgamal.py
@@ -0,0 +1,21 @@
+"""
+data types for 2011/01 Helios
+"""
+
+from helios.datatypes import LDObject
+
+class DiscreteLogProof(LDObject):
+    FIELDS = ['challenge', 'commitment', 'response']
+    STRUCTURED_FIELDS = {
+        'challenge' : 'core/BigInteger',
+        'commitment' : 'core/BigInteger',
+        'response' : 'core/BigInteger'}
+    
+class PublicKey(LDObject):
+    FIELDS = ['y', 'p', 'g', 'q']
+    STRUCTURED_FIELDS = {
+        'y' : 'core/BigInteger',
+        'p' : 'core/BigInteger',
+        'g' : 'core/BigInteger',
+        'q' : 'core/BigInteger'}
+
diff --git a/helios/tests.py b/helios/tests.py
index a6e52525e7717534c168daf53501e2c169aa18e0..7838baf3262f1d9564332e768b4cdf07ad485c66 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -98,7 +98,7 @@ class ElectionModelTests(TestCase):
 
         self.assertTrue(self.election.has_helios_trustee())
 
-        trustee = self.election.get_helios_trustee()        
+        trustee = self.election.get_helios_trustee()
         self.assertNotEquals(trustee, None)
 
     def test_log(self):
@@ -221,10 +221,13 @@ class DatatypeTests(TestCase):
 
     def setUp(self):
         self.election = models.Election.objects.all()[0]
+        self.election.generate_trustee(ELGAMAL_PARAMS)
 
     def test_instantiate(self):
-        ld_obj = datatypes.LDObject.instantiate(self.election)
-
+        ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee')
+        foo = ld_obj.serialize()
+        
+        
 
 ##
 ## Black box tests