diff --git a/helios/crypto/elgamal.py b/helios/crypto/elgamal.py
index 0772f18515718e388f8fbe8a3b90905f7e9df6c5..95e8e28de3d596a859f3c576880b6f341c5acf94 100644
--- a/helios/crypto/elgamal.py
+++ b/helios/crypto/elgamal.py
@@ -510,7 +510,7 @@ class ZKDisjunctiveProof:
     self.proofs = proofs  
 
 class DLogProof(object):
-  def __init__(self, commitment, challenge, response):
+  def __init__(self, commitment=None, challenge=None, response=None):
     self.commitment = commitment
     self.challenge = challenge
     self.response = response
diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py
index 6c2195da64456f51c7348346c612ec9673624821..d3b5271ef918e7baefc4758b1617babd41964742 100644
--- a/helios/datatypes/__init__.py
+++ b/helios/datatypes/__init__.py
@@ -33,7 +33,7 @@ from helios.crypto import utils as cryptoutils
 ## utility function
 ##
 def recursiveToDict(obj):
-    if not obj:
+    if obj == None:
         return None
 
     if type(obj) == list:
@@ -118,6 +118,7 @@ class LDObject(object):
 
     @classmethod
     def instantiate(cls, obj, datatype=None):
+        "FIXME: should datatype override the object's internal datatype? probably not"
         if isinstance(obj, LDObject):
             return obj
 
@@ -128,47 +129,30 @@ class LDObject(object):
             raise Exception("no datatype found")
 
         # nulls
-        if not obj:
+        if obj == None:
             return None
 
         # the class
         dynamic_cls = get_class(datatype)
 
-        # instantiate it
+        # instantiate it and load data
         return_obj = dynamic_cls(obj)
-
-        # go through the subfields and instantiate them too
-        for subfield_name, subfield_type in dynamic_cls.STRUCTURED_FIELDS.iteritems():
-            try:
-                return_obj.structured_fields[subfield_name] = cls.instantiate(getattr(return_obj.wrapped_obj, subfield_name), datatype = subfield_type)
-            except:
-                import pdb; pdb.set_trace()
+        return_obj.loadData()
 
         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.wrapped_obj, f, new_val)
-            else:
-                setattr(self.wrapped_obj, f, None)
-        
-    def serialize(self):
-        return utils.to_json(self.toDict())
-    
-    def toDict(self, alternate_fields=None):
-        val = {}
-        for f in (alternate_fields or self.FIELDS):
-            # is it a structured subfield?
-            if self.structured_fields.has_key(f):
-                val[f] = recursiveToDict(self.structured_fields[f])
-            else:
-                val[f] = self.process_value_out(f, getattr(self.wrapped_obj, f))
-        return val
+    def _getattr_wrapped(self, attr):
+        return getattr(self.wrapped_obj, attr)
 
-    toJSONDict = toDict
+    def _setattr_wrapped(self, attr, val):
+        setattr(self.wrapped_obj, attr, val)
 
+    def loadData(self):
+        "load data using from the wrapped object"
+        # go through the subfields and instantiate them too
+        for subfield_name, subfield_type in self.STRUCTURED_FIELDS.iteritems():
+            self.structured_fields[subfield_name] = self.instantiate(self._getattr_wrapped(subfield_name), datatype = subfield_type)
+        
     def loadDataFromDict(self, d):
         """
         load data from a dictionary
@@ -182,23 +166,31 @@ class LDObject(object):
         for f in self.FIELDS:
             if f in structured_fields:
                 # a structured ld field, recur
-                try:
-                    sub_ld_object = self.fromDict(d[f], type_hint = self.STRUCTURED_FIELDS[f])
-                except KeyError:
-                    import pdb; pdb.set_trace()
-
+                sub_ld_object = self.fromDict(d[f], type_hint = self.STRUCTURED_FIELDS[f])
                 self.structured_fields[f] = sub_ld_object
 
                 # set the field on the wrapped object too
-                try:
-                    setattr(self.wrapped_obj, f, sub_ld_object.wrapped_obj)
-                except AttributeError:
-                    import pdb; pdb.set_trace()
+                self._setattr_wrapped(f, sub_ld_object.wrapped_obj)
             else:
                 # a simple type
                 new_val = self.process_value_in(f, d[f])
-                setattr(self.wrapped_obj, f, new_val)
+                self._setattr_wrapped(f, new_val)
         
+    def serialize(self):
+        return utils.to_json(self.toDict())
+    
+    def toDict(self, alternate_fields=None):
+        val = {}
+        for f in (alternate_fields or self.FIELDS):
+            # is it a structured subfield?
+            if self.structured_fields.has_key(f):
+                val[f] = recursiveToDict(self.structured_fields[f])
+            else:
+                val[f] = self.process_value_out(f, self._getattr_wrapped(f))
+        return val
+
+    toJSONDict = toDict
+
     @classmethod
     def fromDict(cls, d, type_hint=None):
         # the LD type is either in d or in type_hint
@@ -275,16 +267,20 @@ class BaseArrayOfObjects(LDObject):
     WRAPPED_OBJ_CLASS = list
 
     def __init__(self, wrapped_obj):
-        self.items = []
         super(BaseArrayOfObjects, self).__init__(wrapped_obj)
     
     def toDict(self):
         return [item.toDict() for item in self.items]
 
+    def loadData(self):
+        "go through each item and LD instantiate it, as if it were a structured field"
+        self.items = [self.instantiate(element, datatype= self.ELEMENT_TYPE) for element in self.wrapped_obj]
+
     def loadDataFromDict(self, d):
         "assumes that d is a list"
         # TODO: should we be using ELEMENT_TYPE_CLASS here instead of LDObject?
         self.items = [LDObject.fromDict(element, type_hint = self.ELEMENT_TYPE) for element in d]
+        self.wrapped_obj = [item.wrapped_obj for item in self.items]
         
 
 def arrayOf(element_type):
diff --git a/helios/datatypes/djangofield.py b/helios/datatypes/djangofield.py
new file mode 100644
index 0000000000000000000000000000000000000000..670eb59c345c01d73f67a0184c506ad7c1cc9e4b
--- /dev/null
+++ b/helios/datatypes/djangofield.py
@@ -0,0 +1,81 @@
+"""
+taken from
+
+http://www.djangosnippets.org/snippets/377/
+
+and adapted to LDObject
+"""
+
+import datetime
+from django.db import models
+from django.db.models import signals
+from django.conf import settings
+from django.utils import simplejson as json
+from django.core.serializers.json import DjangoJSONEncoder
+
+from . import LDObject
+
+class LDObjectField(models.TextField):
+    """
+    LDObject is a generic textfield that neatly serializes/unserializes
+    JSON objects seamlessly.
+    
+    deserialization_params added on 2011-01-09 to provide additional hints at deserialization time
+    """
+
+    # Used so to_python() is called
+    __metaclass__ = models.SubfieldBase
+
+    def __init__(self, type_hint=None, **kwargs):
+        self.type_hint = type_hint
+        super(LDObjectField, self).__init__(**kwargs)
+
+    def to_python(self, value):
+        """Convert our string value to LDObject after we load it from the DB"""
+
+        # did we already convert this?
+        if not isinstance(value, basestring):
+            return value
+
+        if  value == None:
+            return None
+
+        # in some cases, we're loading an existing array or dict,
+        # we skip this part but instantiate the LD object
+        if isinstance(value, basestring):
+            try:
+                parsed_value = json.loads(value)
+            except:
+                raise Exception("value is not JSON parseable, that's bad news")
+        else:
+            parsed_value = value
+
+        if parsed_value != None:
+            "we give the wrapped object back because we're not dealing with serialization types"            
+            return_val = LDObject.fromDict(parsed_value, type_hint = self.type_hint).wrapped_obj
+            return return_val
+        else:
+            return None
+
+    def get_prep_value(self, value):
+        """Convert our JSON object to a string before we save"""
+        if isinstance(value, basestring):
+            return value
+
+        if value == None:
+            return None
+
+        # instantiate the proper LDObject to dump it appropriately
+        ld_object = LDObject.instantiate(value, datatype=self.type_hint)
+        return ld_object.serialize()
+
+    def value_to_string(self, obj):
+        value = self._get_val_from_obj(obj)
+        return self.get_db_prep_value(value)
+
+##
+## for schema migration, we have to tell South about JSONField
+## basically that it's the same as its parent class
+##
+from south.modelsinspector import add_introspection_rules
+add_introspection_rules([], ["^helios\.datatypes\.djangofield.LDObjectField"])
diff --git a/helios/datatypes/legacy.py b/helios/datatypes/legacy.py
index e1c0dd89e118835790b949c109f0f349416eb956..8084560e1bde7eb78bc3f613c61270ea0c081094 100644
--- a/helios/datatypes/legacy.py
+++ b/helios/datatypes/legacy.py
@@ -6,18 +6,6 @@ from helios.datatypes import LDObject, arrayOf
 from helios.crypto import elgamal as crypto_elgamal
 from helios.workflows import homomorphic
 
-##
-## utilities
-
-class DictObject(object):
-    def __init__(self, d=None):
-        self.d = d
-        if not self.d:
-            self.d = {}
-        
-    def __getattr__(self, k):
-        return self.d[k]
-
 ##
 ##
 
@@ -25,7 +13,16 @@ class LegacyObject(LDObject):
     WRAPPED_OBJ_CLASS = dict
     USE_JSON_LD = False
 
+class DictObject(object):
+    "when the wrapped object is actually dictionary"
+    def _getattr_wrapped(self, attr):
+        return self.wrapped_obj[attr]
+
+    def _setattr_wrapped(self, attr, val):
+        self.wrapped_obj[attr] = val
+
 class Election(LegacyObject):
+    WRAPPED_OBJ_CLASS = homomorphic.Election
     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']
 
@@ -96,7 +93,7 @@ class Trustee(LegacyObject):
         'public_key' : 'legacy/EGPublicKey',
         'pok': 'legacy/DLogProof',
         'decryption_factors': arrayOf(arrayOf('core/BigInteger')),
-        'decryption_proofs' : arrayOf(arrayOf('legacy/DLogProof'))}
+        'decryption_proofs' : arrayOf(arrayOf('legacy/EGZKProof'))}
 
 class EGPublicKey(LegacyObject):
     WRAPPED_OBJ_CLASS = crypto_elgamal.PublicKey
@@ -121,14 +118,12 @@ class EGCiphertext(LegacyObject):
         'alpha': 'core/BigInteger',
         'beta' : 'core/BigInteger'}
 
-class EGZKProofCommitment(LegacyObject):
+class EGZKProofCommitment(DictObject, LegacyObject):
     FIELDS = ['A', 'B']
     STRUCTURED_FIELDS = {
         'A' : 'core/BigInteger',
         'B' : 'core/BigInteger'}
 
-    def __init__(self, wrapped_obj):
-        super(EGZKProofCommitment, self).__init__(DictObject(wrapped_obj))
     
 class EGZKProof(LegacyObject):
     WRAPPED_OBJ_CLASS = crypto_elgamal.ZKProof
@@ -153,6 +148,7 @@ class EGZKDisjunctiveProof(LegacyObject):
         return super(EGZKDisjunctiveProof, self).toDict()['proofs']
 
 class DLogProof(LegacyObject):
+    WRAPPED_OBJ_CLASS = crypto_elgamal.DLogProof
     FIELDS = ['commitment', 'challenge', 'response']
     STRUCTURED_FIELDS = {
         'commitment' : 'core/BigInteger',
@@ -160,10 +156,10 @@ class DLogProof(LegacyObject):
         'response' : 'core/BigInteger'}
 
     def __init__(self, wrapped_obj):
-        if type(wrapped_obj) == dict:
-            super(DLogProof, self).__init__(DictObject(wrapped_obj))
-        else:
-            super(DLogProof, self).__init__(wrapped_obj)
+        if isinstance(wrapped_obj, dict):
+            import pdb; pdb.set_trace()
+
+        super(DLogProof,self).__init__(wrapped_obj)
 
 class Result(LegacyObject):
     pass
@@ -171,8 +167,12 @@ class Result(LegacyObject):
 class Questions(LegacyObject):
     WRAPPED_OBJ = list
 
-    def __len__(self):
-        return len(self.wrapped_obj)
+    def loadDataFromDict(self, d):
+        self.wrapped_obj = d
+
+    def toDict(self):
+        return self.wrapped_obj
+
 
 class Tally(LegacyObject):
     pass
diff --git a/helios/fixtures/election.json b/helios/fixtures/election.json
index 98330512f56a3aa8a30c298a479e8a0142032161..b219c0d4b7014f779db596e008cfbdf432ff583f 100644
--- a/helios/fixtures/election.json
+++ b/helios/fixtures/election.json
@@ -9,7 +9,7 @@
             "election_type" : "election",
             "use_advanced_audit_features" : true,
             "private_p" : false,
-            "description" : "test descriptoin",
+            "description" : "test description",
             "public_key" : null,
             "private_key" : null,
             "questions" : [],
diff --git a/helios/models.py b/helios/models.py
index 39d97ed58c79f0e0ae5bbb066a312c4799e2cbf3..54aa59e9ba8cbf4b410ba3fdb9f001fd9bf3085e 100644
--- a/helios/models.py
+++ b/helios/models.py
@@ -22,6 +22,7 @@ from helios import datatypes
 # useful stuff in auth
 from auth.models import User, AUTH_SYSTEMS
 from auth.jsonfield import JSONField
+from helios.datatypes.djangofield import LDObjectField
 
 import csv, copy
   
@@ -57,22 +58,18 @@ class Election(HeliosModel):
   private_p = models.BooleanField(default=False, null=False)
 
   description = models.TextField()
-  public_key = JSONField(datatypes.LDObject,
-                         deserialization_params = {'type_hint' : 'legacy/EGPublicKey'},
-                         null=True)
-  private_key = JSONField(datatypes.LDObject,
-                          deserialization_params = {'type_hint' : 'legacy/EGSecretKey'},
-                          null=True)
+  public_key = LDObjectField(type_hint = 'legacy/EGPublicKey',
+                             null=True)
+  private_key = LDObjectField(type_hint = 'legacy/EGSecretKey',
+                              null=True)
   
-  questions = JSONField(datatypes.LDObject,
-                        deserialization_params = {'type_hint' : 'legacy/Questions'},
-                        null=True)
+  questions = LDObjectField(type_hint = 'legacy/Questions',
+                            null=True)
   
   # eligibility is a JSON field, which lists auth_systems and eligibility details for that auth_system, e.g.
   # [{'auth_system': 'cas', 'constraint': [{'year': 'u12'}, {'year':'u13'}]}, {'auth_system' : 'password'}, {'auth_system' : 'openid', 'constraint': [{'host':'http://myopenid.com'}]}]
-  eligibility = JSONField(datatypes.LDObject,
-                          deserialization_params = {'type_hint' : 'legacy/Eligibility'},
-                          null=True)
+  eligibility = LDObjectField(type_hint = 'legacy/Eligibility',
+                              null=True)
 
   # open registration?
   # this is now used to indicate the state of registration,
@@ -124,14 +121,12 @@ class Election(HeliosModel):
   
   # encrypted tally, each a JSON string
   # used only for homomorphic tallies
-  encrypted_tally = JSONField(datatypes.LDObject,
-                              deserialization_params={'type_hint': 'legacy/Tally'},
-                              null=True)
+  encrypted_tally = LDObjectField(type_hint = 'legacy/Tally',
+                                  null=True)
 
   # results of the election
-  result = JSONField(datatypes.LDObject,
-                     deserialization_params = {'type_hint' : 'legacy/Result'},
-                     null=True)
+  result = LDObjectField(type_hint = 'legacy/Result',
+                         null=True)
 
   # decryption proof, a JSON object
   # no longer needed since it's all trustees
@@ -409,12 +404,13 @@ class Election(HeliosModel):
     trustee.uuid = str(uuid.uuid4())
     trustee.name = settings.DEFAULT_FROM_NAME
     trustee.email = settings.DEFAULT_FROM_EMAIL
-    trustee.public_key = datatypes.LDObject.instantiate(keypair.pk, 'pkc/elgamal/PublicKey')
-    trustee.secret_key = datatypes.LDObject.instantiate(keypair.sk, 'pkc/elgamal/SecretKey')
+    trustee.public_key = keypair.pk
+    trustee.secret_key = keypair.sk
     
-    # FIXME: compute it
-    trustee.public_key_hash = utils.hash_b64(trustee.public_key.serialize())
-    trustee.pok = datatypes.LDObject.instantiate(trustee.secret_key.wrapped_obj.prove_sk(algs.DLog_challenge_generator), 'pkc/elgamal/DLogProof')
+    # FIXME: is this at the right level of abstraction?
+    trustee.public_key_hash = datatypes.LDObject.instantiate(trustee.public_key, datatype='legacy/EGPublicKey').hash
+
+    trustee.pok = trustee.secret_key.prove_sk(algs.DLog_challenge_generator)
 
     trustee.save()
 
@@ -599,9 +595,8 @@ class Voter(HeliosModel):
   alias = models.CharField(max_length = 100, null=True)
   
   # we keep a copy here for easy tallying
-  vote = JSONField(datatypes.LDObject,
-                   deserialization_params = {'type_hint': 'legacy/EncryptedVote'},
-                   null=True)
+  vote = LDObjectField(type_hint = 'legacy/EncryptedVote',
+                       null=True)
   vote_hash = models.CharField(max_length = 100, null=True)
   cast_at = models.DateTimeField(auto_now_add=False, null=True)
 
@@ -762,8 +757,7 @@ class CastVote(HeliosModel):
   voter = models.ForeignKey(Voter)
   
   # the actual encrypted vote
-  vote = JSONField(datatypes.LDObject,
-                   deserialization_params = {'type_hint' : 'legacy/EncryptedVote'})
+  vote = LDObjectField(type_hint = 'legacy/EncryptedVote')
 
   # cache the hash of the vote
   vote_hash = models.CharField(max_length=100)
@@ -883,31 +877,26 @@ class Trustee(HeliosModel):
   secret = models.CharField(max_length=100)
   
   # public key
-  public_key = JSONField(datatypes.LDObject,
-                         deserialization_params = {'type_hint': 'legacy/EGPublicKey'},
-                         null=True)
+  public_key = LDObjectField(type_hint = 'legacy/EGPublicKey',
+                             null=True)
   public_key_hash = models.CharField(max_length=100)
 
   # secret key
   # if the secret key is present, this means
   # Helios is playing the role of the trustee.
-  secret_key = JSONField(datatypes.LDObject,
-                         deserialization_params = {'type_hint': 'legacy/EGSecretKey'},
-                         null=True)
+  secret_key = LDObjectField(type_hint = 'legacy/EGSecretKey',
+                             null=True)
   
   # proof of knowledge of secret key
-  pok = JSONField(datatypes.LDObject,
-                  deserialization_params = {'type_hint': 'legacy/DLogProof'},
-                  null=True)
+  pok = LDObjectField(type_hint = 'legacy/DLogProof',
+                      null=True)
   
   # decryption factors
-  decryption_factors = JSONField(datatypes.LDObject,
-                                 deserialization_params = {'type_hint' : datatypes.arrayOf(datatypes.arrayOf('core/BigInteger'))},
-                                 null=True)
+  decryption_factors = LDObjectField(type_hint = datatypes.arrayOf(datatypes.arrayOf('core/BigInteger')),
+                                     null=True)
 
-  decryption_proofs = JSONField(datatypes.LDObject,
-                                deserialization_params = {'type_hint' : datatypes.arrayOf(datatypes.arrayOf('legacy/EGZKProof'))},
-                                null=True)
+  decryption_proofs = LDObjectField(type_hint = datatypes.arrayOf(datatypes.arrayOf('legacy/EGZKProof')),
+                                    null=True)
   
   def save(self, *args, **kwargs):
     """
diff --git a/helios/tests.py b/helios/tests.py
index 88235accac67f558e9b74aebac5b23ab102eb419..d4e3f03a3acd499742b00278c7dc58da04a64ce2 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -233,6 +233,14 @@ class DatatypeTests(TestCase):
                 'p' : '23434',
                 'g' : '2343243242',
                 'q' : '2343242343434'}, type_hint = 'pkc/elgamal/PublicKey')
+
+    def test_dictobject_from_dict(self):
+        original_dict = {
+            'A' : '35423432',
+            'B' : '234324243'}
+        ld_obj = datatypes.LDObject.fromDict(original_dict, type_hint = 'legacy/EGZKProofCommitment')
+
+        self.assertEquals(original_dict, ld_obj.toDict())
         
         
         
diff --git a/helios/views.py b/helios/views.py
index 6fe14473b75b0da19d069051260a8380ff749d4d..c71bf3c425feff89db00003ad0b21588b95974d9 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -319,7 +319,7 @@ def socialbuttons(request):
 @election_view()
 def list_trustees(request, election):
   trustees = Trustee.get_by_election(election)
-  return [t.toJSONDict() for t in trustees]
+  return [datatypes.LDObject.instantiate(t, 'legacy/Trustee').toDict() for t in trustees]
   
 @election_view()
 def list_trustees_view(request, election):
@@ -802,7 +802,7 @@ def one_election_register(request, election):
 def one_election_save_questions(request, election):
   check_csrf(request)
   
-  election.questions = datatypes.LDObject.instantiate(utils.from_json(request.POST['questions_json']), 'legacy/Questions');
+  election.questions = utils.from_json(request.POST['questions_json'])
   election.save()
 
   # always a machine API
@@ -1172,7 +1172,7 @@ def ballot_list(request, election):
     after = datetime.datetime.strptime(request.GET['after'], '%Y-%m-%d %H:%M:%S')
     
   voters = Voter.get_by_election(election, cast=True, order_by='cast_at', limit=limit, after=after)
-  return [v.last_cast_vote().toJSONDict() for v in voters]
+  return [datatypes.LDObject.instantiate(v.last_cast_vote(), 'legacy/ShortCastVote').toDict() for v in voters]