diff --git a/wa/utils/types.py b/wa/utils/types.py
index 27177a3e..98e2b079 100644
--- a/wa/utils/types.py
+++ b/wa/utils/types.py
@@ -32,6 +32,7 @@ import numbers
 import shlex
 import string
 from bisect import insort
+from urllib import quote, unquote
 from collections import defaultdict, MutableMapping
 from copy import copy
 
@@ -327,7 +328,7 @@ class prioritylist(object):
             raise ValueError('Invalid index {}'.format(index))
         current_global_offset = 0
         priority_counts = {priority: count for (priority, count) in
-                           zip(self.priorities, [len(self.elements[p]) 
+                           zip(self.priorities, [len(self.elements[p])
                                                  for p in self.priorities])}
         for priority in self.priorities:
             if not index_range:
@@ -586,3 +587,118 @@ def enum(args, start=0, step=1):
 
     return Enum
 
+
+class ParameterDict(dict):
+    """
+    A dict-like object that automatically encodes various types into a url safe string,
+    and enforces a single type for the contents in a list.
+    Each value is first prefixed with 2 letters to preserve type when encoding to a string.
+    The format used is "value_type, value_dimension" e.g a 'list of floats' would become 'fl'.
+    """
+
+    # Function to determine the appropriate prefix based on the parameters type
+    @staticmethod
+    def _get_prefix(obj):
+        if isinstance(obj, basestring):
+            prefix = 's'
+        elif isinstance(obj, float):
+            prefix = 'f'
+        elif isinstance(obj, long):
+            prefix = 'd'
+        elif isinstance(obj, bool):
+            prefix = 'b'
+        elif isinstance(obj, int):
+            prefix = 'i'
+        elif obj is None:
+            prefix = 'n'
+        else:
+            raise ValueError('Unable to encode {} {}'.format(obj, type(obj)))
+        return prefix
+
+    # Function to add prefix and urlencode a provided parameter.
+    @staticmethod
+    def _encode(obj):
+        if isinstance(obj, list):
+            t = type(obj[0])
+            prefix = ParameterDict._get_prefix(obj[0]) + 'l'
+            for item in obj:
+                if not isinstance(item, t):
+                    msg = 'Lists must only contain a single type, contains {} and {}'
+                    raise ValueError(msg.format(t, type(item)))
+            obj = '0newelement0'.join(str(x) for x in obj)
+        else:
+            prefix = ParameterDict._get_prefix(obj) + 's'
+        return quote(prefix + str(obj))
+
+    # Function to decode a string and return a value of the original parameter type.
+    # pylint: disable=too-many-return-statements
+    @staticmethod
+    def _decode(string):
+        value_type = string[:1]
+        value_dimension = string[1:2]
+        value = unquote(string[2:])
+        if value_dimension == 's':
+            if value_type == 's':
+                return str(value)
+            elif value_type == 'b':
+                return boolean(value)
+            elif value_type == 'd':
+                return long(value)
+            elif value_type == 'f':
+                return float(value)
+            elif value_type == 'i':
+                return int(value)
+            elif value_type == 'n':
+                return None
+        elif value_dimension == 'l':
+            return [ParameterDict._decode(value_type + 's' + x)
+                    for x in value.split('0newelement0')]
+        else:
+            raise ValueError('Unknown {} {}'.format(type(string), string))
+
+    def __init__(self, *args, **kwargs):
+        for k, v in kwargs.iteritems():
+            self.__setitem__(k, v)
+        dict.__init__(self, *args)
+
+    def __setitem__(self, name, value):
+        dict.__setitem__(self, name, self._encode(value))
+
+    def __getitem__(self, name):
+        return self._decode(dict.__getitem__(self, name))
+
+    def __contains__(self, item):
+        return dict.__contains__(self, self._encode(item))
+
+    def __iter__(self):
+        return iter((k, self._decode(v)) for (k, v) in self.items())
+
+    def iteritems(self):
+        return self.__iter__()
+
+    def get(self, name):
+        return self._decode(dict.get(self, name))
+
+    def pop(self, key):
+        return self._decode(dict.pop(self, key))
+
+    def popitem(self):
+        key, value = dict.popitem(self)
+        return (key, self._decode(value))
+
+    def iter_encoded_items(self):
+        return dict.iteritems(self)
+
+    def get_encoded_value(self, name):
+        return dict.__getitem__(self, name)
+
+    def values(self):
+        return [self[k] for k in dict.keys(self)]
+
+    def update(self, *args, **kwargs):
+        for d in list(args) + [kwargs]:
+            if isinstance(d, ParameterDict):
+                dict.update(self, d)
+            else:
+                for k, v in d.iteritems():
+                    self[k] = v