Repository: DOV-Vlaanderen/pydov

Issue:
Use gzip to compress cached files
When downloading larger XML files, this can save quite a lot of diskspace.

Discussion:

Patch:
diff --git a/docs/caching.rst b/docs/caching.rst
index 0579e24..b3811dd 100644
--- a/docs/caching.rst
+++ b/docs/caching.rst
@@ -47,18 +47,18 @@ your own cache, as follows::
 
     import pydov.util.caching
 
-    pydov.cache = pydov.util.caching.TransparentCache(
+    pydov.cache = pydov.util.caching.GzipTextFileCache(
         cachedir=r'C:\temp\pydov'
     )
 
 Besides controlling the cache's location, this also allows using a different
 cache in different scripts or projects.
 
-Mind that xmls are stored by search type because permalinks are not unique
+Mind that xmls are stored by search type because object keys are not unique
 across types. Therefore, the dir structure of the cache will look like, e.g.::
 
-    ...\pydov\boring\filename.xml
-    ...\pydov\filter\filename.xml
+    ...\pydov\boring\filename.xml.gz
+    ...\pydov\filter\filename.xml.gz
 
 
 Changing the maximum age of cached data
@@ -71,7 +71,7 @@ be considered valid for the current runtime::
     import pydov.util.caching
     import datetime
 
-    pydov.cache = pydov.util.caching.TransparentCache(
+    pydov.cache = pydov.util.caching.GzipTextFileCache(
         max_age=datetime.timedelta(days=1)
     )
 
@@ -112,3 +112,52 @@ by issuing::
 
 This will erase the entire cache, not only the records older than the
 maximum age.
+
+Custom caching
+**************
+
+Using plain text caching
+........................
+
+By default, pydov caches files on disk as gzipped XML documents. Should you
+for any reason want to use plain text XML documents instead, you can do so by
+using the PlainTextFileCache instead of the GzipTextFileCache.
+
+Mind that this can increase the disk usage of the cache by 10x.::
+
+    import pydov.util.caching
+
+    pydov.cache = pydov.util.caching.PlainTextFileCache()
+
+
+Implementing custom caching
+...........................
+
+Should you want to implement your own caching mechanism, you can do so by
+subclassing AbstractCache and implementing its abstract methods ``get``,
+``clean`` and ``remove``. Hereby you can use the available methods
+``_get_remote`` to request data from the DOV webservices and
+``_emit_cache_hit`` to notify hooks a file has been retrieved from the cache.
+
+A (naive) implementation for an in-memory cache would be something like::
+
+    from pydov.util.caching import AbstractCache
+
+    class MemoryCache(AbstractCache):
+        def __init__(self):
+            self.cache = {}
+
+        def get(self, url):
+            if url not in self.cache:
+                self.cache[url] = self._get_remote(url)
+            else:
+                self._emit_cache_hit(url)
+            return self.cache[url]
+
+        def clean(self):
+            self.cache = {}
+
+        def remove(self):
+            self.cache = {}
+
+    pydov.cache = MemoryCache()
diff --git a/docs/notebooks/caching.ipynb b/docs/notebooks/caching.ipynb
index 88403dc..0a581d7 100644
--- a/docs/notebooks/caching.ipynb
+++ b/docs/notebooks/caching.ipynb
@@ -126,10 +126,10 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[000/110] ..................................................\n",
-      "[050/110] ..................................................\n",
-      "[100/110] ..........\n",
-      "Wall time: 35.8 s\n"
+      "[000/111] ..................................................\n",
+      "[050/111] ..................................................\n",
+      "[100/111] ...........\n",
+      "Wall time: 38.4 s\n"
      ]
     }
    ],
@@ -149,8 +149,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "('number of files: ', 110)\n",
-      "('files present: ', ['1879-119364.xml', '1879-121292.xml', '1879-121293.xml', '1879-121387.xml', '1879-121401.xml', '1879-121412.xml', '1879-121424.xml', '1879-122256.xml', '1894-121258.xml', '1894-122153.xml', '1894-122154.xml', '1894-122155.xml', '1895-121232.xml', '1895-121241.xml', '1895-121242.xml', '1895-121244.xml', '1895-121247.xml', '1895-121248.xml', '1923-121199.xml', '1923-121200.xml', '1932-121315.xml', '1936-122224.xml', '1938-121359.xml', '1938-121360.xml', '1953-121327.xml', '1953-121361.xml', '1953-121362.xml', '1969-033206.xml', '1969-033207.xml', '1969-033208.xml', '1969-033209.xml', '1969-033211.xml', '1969-033212.xml', '1969-033213.xml', '1969-033214.xml', '1969-033215.xml', '1969-033216.xml', '1969-033217.xml', '1969-033218.xml', '1969-033219.xml', '1969-033220.xml', '1969-092685.xml', '1969-092686.xml', '1969-092687.xml', '1969-092688.xml', '1969-092689.xml', '1970-018757.xml', '1970-018762.xml', '1970-018763.xml', '1970-061362.xml', '1970-061363.xml', '1970-061364.xml', '1970-061365.xml', '1970-061366.xml', '1970-061442.xml', '1970-061443.xml', '1970-061444.xml', '1970-061445.xml', '1970-061446.xml', '1970-061447.xml', '1970-061450.xml', '1970-061454.xml', '1970-104897.xml', '1970-104898.xml', '1970-104899.xml', '1970-104900.xml', '1973-018152.xml', '1973-060207.xml', '1973-060208.xml', '1973-081811.xml', '1973-104723.xml', '1973-104727.xml', '1973-104728.xml', '1974-010351.xml', '1975-010345.xml', '1976-014856.xml', '1976-015297.xml', '1976-015298.xml', '1976-015779.xml', '1976-015780.xml', '1976-015781.xml', '1976-015782.xml', '1978-012352.xml', '1978-121458.xml', '1984-081833.xml', '1984-081834.xml', '1985-084552.xml', '1986-005594.xml', '1986-005596.xml', '1986-005597.xml', '1986-005598.xml', '1986-059814.xml', '1986-059815.xml', '1986-059816.xml', '1987-119382.xml', '1996-021717.xml', '1996-081802.xml', '2017-148854.xml', '2017-152011.xml', '2017-153161.xml', '2018-153957.xml', '2018-154057.xml', '2018-155266.xml', '2018-155580.xml', '2018-156632.xml', '2018-156633.xml', '2018-156634.xml', '2018-157193.xml', '2018-157294.xml', '2018-157386.xml'])\n"
+      "('number of files: ', 111)\n",
+      "('files present: ', ['1879-119364.xml.gz', '1879-121292.xml.gz', '1879-121293.xml.gz', '1879-121387.xml.gz', '1879-121401.xml.gz', '1879-121412.xml.gz', '1879-121424.xml.gz', '1879-122256.xml.gz', '1894-121258.xml.gz', '1894-122153.xml.gz', '1894-122154.xml.gz', '1894-122155.xml.gz', '1895-121232.xml.gz', '1895-121241.xml.gz', '1895-121242.xml.gz', '1895-121244.xml.gz', '1895-121247.xml.gz', '1895-121248.xml.gz', '1923-121199.xml.gz', '1923-121200.xml.gz', '1932-121315.xml.gz', '1936-122224.xml.gz', '1938-121359.xml.gz', '1938-121360.xml.gz', '1953-121327.xml.gz', '1953-121361.xml.gz', '1953-121362.xml.gz', '1969-033206.xml.gz', '1969-033207.xml.gz', '1969-033208.xml.gz', '1969-033209.xml.gz', '1969-033211.xml.gz', '1969-033212.xml.gz', '1969-033213.xml.gz', '1969-033214.xml.gz', '1969-033215.xml.gz', '1969-033216.xml.gz', '1969-033217.xml.gz', '1969-033218.xml.gz', '1969-033219.xml.gz', '1969-033220.xml.gz', '1969-092685.xml.gz', '1969-092686.xml.gz', '1969-092687.xml.gz', '1969-092688.xml.gz', '1969-092689.xml.gz', '1970-018757.xml.gz', '1970-018762.xml.gz', '1970-018763.xml.gz', '1970-061362.xml.gz', '1970-061363.xml.gz', '1970-061364.xml.gz', '1970-061365.xml.gz', '1970-061366.xml.gz', '1970-061442.xml.gz', '1970-061443.xml.gz', '1970-061444.xml.gz', '1970-061445.xml.gz', '1970-061446.xml.gz', '1970-061447.xml.gz', '1970-061450.xml.gz', '1970-061454.xml.gz', '1970-104897.xml.gz', '1970-104898.xml.gz', '1970-104899.xml.gz', '1970-104900.xml.gz', '1973-018152.xml.gz', '1973-060207.xml.gz', '1973-060208.xml.gz', '1973-081811.xml.gz', '1973-104723.xml.gz', '1973-104727.xml.gz', '1973-104728.xml.gz', '1974-010351.xml.gz', '1975-010345.xml.gz', '1976-014856.xml.gz', '1976-015297.xml.gz', '1976-015298.xml.gz', '1976-015779.xml.gz', '1976-015780.xml.gz', '1976-015781.xml.gz', '1976-015782.xml.gz', '1978-012352.xml.gz', '1978-121458.xml.gz', '1984-081833.xml.gz', '1984-081834.xml.gz', '1985-084552.xml.gz', '1986-005594.xml.gz', '1986-005596.xml.gz', '1986-005597.xml.gz', '1986-005598.xml.gz', '1986-059814.xml.gz', '1986-059815.xml.gz', '1986-059816.xml.gz', '1987-119382.xml.gz', '1996-021717.xml.gz', '1996-081802.xml.gz', '2017-148854.xml.gz', '2017-152011.xml.gz', '2017-153161.xml.gz', '2018-153957.xml.gz', '2018-154057.xml.gz', '2018-155266.xml.gz', '2018-155580.xml.gz', '2018-156632.xml.gz', '2018-156633.xml.gz', '2018-156634.xml.gz', '2018-157193.xml.gz', '2018-157294.xml.gz', '2018-157386.xml.gz', '2019-160294.xml.gz'])\n"
      ]
     }
    ],
@@ -178,10 +178,10 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[000/110] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
-      "[050/110] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
-      "[100/110] cccccccccc\n",
-      "Wall time: 1.07 s\n"
+      "[000/111] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[050/111] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[100/111] ccccccccccc\n",
+      "Wall time: 980 ms\n"
      ]
     }
    ],
@@ -221,7 +221,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "('number of files: ', 110)\n"
+      "('number of files: ', 111)\n"
      ]
     }
    ],
@@ -276,7 +276,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "('number of files: ', 110)\n"
+      "('number of files: ', 111)\n"
      ]
     }
    ],
@@ -336,7 +336,7 @@
    "source": [
     "import pydov.util.caching\n",
     "\n",
-    "pydov.cache = pydov.util.caching.TransparentCache(\n",
+    "pydov.cache = pydov.util.caching.GzipTextFileCache(\n",
     "    cachedir=r'C:\\temp\\pydov'\n",
     "    )"
    ]
@@ -362,7 +362,9 @@
   {
    "cell_type": "code",
    "execution_count": 13,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "# for the sake of the example, change dir location back \n",
@@ -402,7 +404,7 @@
    "source": [
     "import pydov.util.caching\n",
     "import datetime\n",
-    "pydov.cache = pydov.util.caching.TransparentCache(\n",
+    "pydov.cache = pydov.util.caching.GzipTextFileCache(\n",
     "    max_age=datetime.timedelta(seconds=1)\n",
     "    )\n",
     "print(pydov.cache.max_age)"
@@ -417,13 +419,13 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "1879-119364.xml\n"
+      "1879-119364.xml.gz\n"
      ]
     },
     {
      "data": {
       "text/plain": [
-       "'Tue Oct 30 16:16:34 2018'"
+       "'Wed Mar 06 14:36:24 2019'"
       ]
      },
      "execution_count": 15,
@@ -450,10 +452,10 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[000/110] ..................................................\n",
-      "[050/110] ..................................................\n",
-      "[100/110] ..........\n",
-      "Wall time: 30.8 s\n"
+      "[000/111] ..................................................\n",
+      "[050/111] ..................................................\n",
+      "[100/111] ...........\n",
+      "Wall time: 35.7 s\n"
      ]
     }
    ],
@@ -471,13 +473,13 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "1879-119364.xml\n"
+      "1879-119364.xml.gz\n"
      ]
     },
     {
      "data": {
       "text/plain": [
-       "'Tue Oct 30 16:19:01 2018'"
+       "'Wed Mar 06 14:38:20 2019'"
       ]
      },
      "execution_count": 17,
@@ -536,7 +538,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "('number of files before clean: ', 110)\n",
+      "('number of files before clean: ', 111)\n",
       "('number of files after clean: ', 0)\n"
      ]
     }
@@ -573,15 +575,6 @@
     "# check existence of the cache directory:\n",
     "print(os.path.exists(os.path.join(cachedir, 'boring')))"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/pydov/__init__.py b/pydov/__init__.py
index 8e0a5f7..f5c1994 100644
--- a/pydov/__init__.py
+++ b/pydov/__init__.py
@@ -5,7 +5,7 @@ from pydov.util.hooks import SimpleStatusHook
 __author__ = """DOV-Vlaanderen"""
 __version__ = '0.1.0'
 
-cache = pydov.util.caching.TransparentCache()
+cache = pydov.util.caching.GzipTextFileCache()
 
 hooks = [
     SimpleStatusHook(),
diff --git a/pydov/util/caching.py b/pydov/util/caching.py
index 3bc4cdc..f87815c 100644
--- a/pydov/util/caching.py
+++ b/pydov/util/caching.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 """Module implementing a local cache for downloaded XML files."""
 import datetime
+import gzip
 import os
 import re
 import shutil
@@ -11,8 +12,75 @@ import pydov
 from pydov.util.dovutil import get_dov_xml
 
 
-class TransparentCache(object):
-    """Class for transparent caching of downloaded XML files from DOV."""
+class AbstractCache(object):
+    def _get_remote(self, url):
+        """Get the XML data by requesting it from the given URL.
+
+        Parameters
+        ----------
+        url : str
+            Permanent URL to a DOV object.
+
+        Returns
+        -------
+        xml : bytes
+            The raw XML data of this DOV object as bytes.
+
+        """
+        xml = get_dov_xml(url)
+        for hook in pydov.hooks:
+            hook.xml_downloaded(url.rstrip('.xml'))
+        return xml
+
+    def _emit_cache_hit(self, url):
+        """Emit the XML cache hit event for all registered hooks.
+
+        Parameters
+        ----------
+        url : str
+            Permanent URL to a DOV object.
+
+        """
+        for hook in pydov.hooks:
+            hook.xml_cache_hit(url.rstrip('.xml'))
+
+    def get(self, url):
+        """Get the XML data for the DOV object referenced by the given URL.
+
+        If a valid version exists in the cache, it will be loaded and
+        returned. If no valid version exists, the XML will be downloaded
+        from the DOV webservice, saved in the cache and returned.
+
+        Parameters
+        ----------
+        url : str
+            Permanent URL to a DOV object.
+
+        Returns
+        -------
+        xml : bytes
+            The raw XML data of this DOV object as bytes.
+
+        """
+        raise NotImplementedError
+
+    def clean(self):
+        """Clean the cache by removing old records from the cache.
+
+        Since during normal use the cache only grows by adding new objects and
+        overwriting existing ones with a new version, you can use this
+        function to clean the cache. It will remove all records older than
+        the maximum age from the cache.
+
+        """
+        raise NotImplementedError
+
+    def remove(self):
+        """Remove the entire cache."""
+        raise NotImplementedError
+
+
+class AbstractFileCache(AbstractCache):
     def __init__(self, max_age=datetime.timedelta(weeks=2), cachedir=None):
         """Initialisation.
 
@@ -47,7 +115,26 @@ class TransparentCache(object):
         except Exception:
             pass
 
-    def _get_type_key(self, url):
+    def _get_filepath(self, datatype, key):
+        """Get the location on disk where the object with given datatype and
+        key is to be saved.
+
+        Parameters
+        ----------
+        datatype : str
+            Datatype of the DOV object.
+        key : str
+            Unique and permanent object key of the DOV object.
+
+        Returns
+        -------
+        str
+            Full absolute path on disk where the object is to be saved.
+
+        """
+        raise NotImplementedError
+
+    def _get_type_key_from_url(self, url):
         """Parse a DOV permalink and return the datatype and object key.
 
         Parameters
@@ -68,29 +155,26 @@ class TransparentCache(object):
         if datatype and len(datatype.groups()) > 1:
             return datatype.group(1), datatype.group(2)
 
-    def _save(self, datatype, key, content):
-        """Save the given content in the cache.
+    def _get_type_key_from_path(self, path):
+        """Parse a filepath and return the datatype and object key.
 
         Parameters
         ----------
+        path : str
+            Full, absolute, path to a cached file.
+
+        Returns
+        -------
         datatype : str
-            Datatype of the DOV object to save.
+            Datatype of the DOV object referred to by the URL.
         key : str
-            Unique and permanent object key of the DOV object to save.
-        content : : bytes
-            The raw XML data of this DOV object as bytes.
+            Unique and permanent key of the instance of the DOV object
+            referred to by the URL.
 
         """
-        folder = os.path.join(self.cachedir, datatype)
+        raise NotImplementedError
 
-        if not os.path.exists(folder):
-            os.makedirs(folder)
-
-        filepath = os.path.join(folder, key + '.xml')
-        with open(filepath, 'w', encoding='utf-8') as f:
-            f.write(content.decode('utf-8'))
-
-    def _valid(self, datatype, key):
+    def _is_valid(self, datatype, key):
         """Check if a valid version of the given DOV object exists in the
         cache.
 
@@ -110,7 +194,7 @@ class TransparentCache(object):
             True if a valid cached version exists, False otherwise.
 
         """
-        filepath = os.path.join(self.cachedir, datatype, key + '.xml')
+        filepath = self._get_filepath(datatype, key)
         if not os.path.exists(filepath):
             return False
 
@@ -137,28 +221,22 @@ class TransparentCache(object):
             XML string of the DOV object, loaded from the cache.
 
         """
-        filepath = os.path.join(self.cachedir, datatype, key + '.xml')
-        with open(filepath, 'r', encoding='utf-8') as f:
-            return f.read()
+        raise NotImplementedError
 
-    def _get_remote(self, url):
-        """Get the XML data by requesting it from the given URL.
+    def _save(self, datatype, key, content):
+        """Save the given content in the cache.
 
         Parameters
         ----------
-        url : str
-            Permanent URL to a DOV object.
-
-        Returns
-        -------
-        xml : bytes
+        datatype : str
+            Datatype of the DOV object to save.
+        key : str
+            Unique and permanent object key of the DOV object to save.
+        content : bytes
             The raw XML data of this DOV object as bytes.
 
         """
-        xml = get_dov_xml(url)
-        for hook in pydov.hooks:
-            hook.xml_downloaded(url.rstrip('.xml'))
-        return xml
+        raise NotImplementedError
 
     def get(self, url):
         """Get the XML data for the DOV object referenced by the given URL.
@@ -178,12 +256,11 @@ class TransparentCache(object):
             The raw XML data of this DOV object as bytes.
 
         """
-        datatype, key = self._get_type_key(url)
+        datatype, key = self._get_type_key_from_url(url)
 
-        if self._valid(datatype, key):
+        if self._is_valid(datatype, key):
             try:
-                for hook in pydov.hooks:
-                    hook.xml_cache_hit(url.rstrip('.xml'))
+                self._emit_cache_hit(url)
                 return self._load(datatype, key).encode('utf-8')
             except Exception:
                 pass
@@ -211,8 +288,11 @@ class TransparentCache(object):
         if os.path.exists(self.cachedir):
             for type in os.listdir(self.cachedir):
                 for object in os.listdir(os.path.join(self.cachedir, type)):
-                    if not self._valid(type, object.rstrip('.xml')):
-                        os.remove(os.path.join(self.cachedir, type, object))
+                    datatype, key = self._get_type_key_from_path(
+                        os.path.join(self.cachedir, type, object))
+                    if not self._is_valid(datatype, key):
+                        os.remove(
+                            os.path.join(self.cachedir, datatype, object))
 
     def remove(self):
         """Remove the entire cache directory.
@@ -227,3 +307,171 @@ class TransparentCache(object):
         """
         if os.path.exists(self.cachedir):
             shutil.rmtree(self.cachedir)
+
+
+class PlainTextFileCache(AbstractFileCache):
+    """Class for plain text caching of downloaded XML files from DOV."""
+
+    def _get_filepath(self, datatype, key):
+        """Get the location on disk where the object with given datatype and
+        key is to be saved.
+
+        Parameters
+        ----------
+        datatype : str
+            Datatype of the DOV object.
+        key : str
+            Unique and permanent object key of the DOV object.
+
+        Returns
+        -------
+        str
+            Full absolute path on disk where the object is to be saved.
+
+        """
+        return os.path.join(self.cachedir, datatype, key + '.xml')
+
+    def _get_type_key_from_path(self, path):
+        """Parse a filepath and return the datatype and object key.
+
+        Parameters
+        ----------
+        path : str
+            Full, absolute, path to a cached file.
+
+        Returns
+        -------
+        datatype : str
+            Datatype of the DOV object referred to by the URL.
+        key : str
+            Unique and permanent key of the instance of the DOV object
+            referred to by the URL.
+
+        """
+        key = os.path.basename(path).rstrip('.xml')
+        datatype = os.path.dirname(path).split()[-1]
+        return datatype, key
+
+    def _save(self, datatype, key, content):
+        """Save the given content in the cache.
+
+        Parameters
+        ----------
+        datatype : str
+            Datatype of the DOV object to save.
+        key : str
+            Unique and permanent object key of the DOV object to save.
+        content : bytes
+            The raw XML data of this DOV object as bytes.
+
+        """
+        filepath = self._get_filepath(datatype, key)
+        folder = os.path.dirname(filepath)
+
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+
+        with open(filepath, 'w', encoding='utf-8') as f:
+            f.write(content.decode('utf-8'))
+
+    def _load(self, datatype, key):
+        """Read a cached version from disk.
+
+        datatype : str
+            Datatype of the DOV object.
+        key : str
+            Unique and permanent object key of the DOV object.
+
+        Returns
+        -------
+        str (xml)
+            XML string of the DOV object, loaded from the cache.
+
+        """
+        filepath = self._get_filepath(datatype, key)
+        with open(filepath, 'r', encoding='utf-8') as f:
+            return f.read()
+
+
+class GzipTextFileCache(AbstractFileCache):
+    """Class for GZipped text caching of downloaded XML files from DOV."""
+
+    def _get_filepath(self, datatype, key):
+        """Get the location on disk where the object with given datatype and
+        key is to be saved.
+
+        Parameters
+        ----------
+        datatype : str
+            Datatype of the DOV object.
+        key : str
+            Unique and permanent object key of the DOV object.
+
+        Returns
+        -------
+        str
+            Full absolute path on disk where the object is to be saved.
+
+        """
+        return os.path.join(self.cachedir, datatype, key + '.xml.gz')
+
+    def _get_type_key_from_path(self, path):
+        """Parse a filepath and return the datatype and object key.
+
+        Parameters
+        ----------
+        path : str
+            Full, absolute, path to a cached file.
+
+        Returns
+        -------
+        datatype : str
+            Datatype of the DOV object referred to by the URL.
+        key : str
+            Unique and permanent key of the instance of the DOV object
+            referred to by the URL.
+
+        """
+        key = os.path.basename(path).rstrip('.xml.gz')
+        datatype = os.path.dirname(path).split()[-1]
+        return datatype, key
+
+    def _save(self, datatype, key, content):
+        """Save the given content in the cache.
+
+        Parameters
+        ----------
+        datatype : str
+            Datatype of the DOV object to save.
+        key : str
+            Unique and permanent object key of the DOV object to save.
+        content : bytes
+            The raw XML data of this DOV object as bytes.
+
+        """
+        filepath = self._get_filepath(datatype, key)
+        folder = os.path.dirname(filepath)
+
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+
+        with gzip.open(filepath, 'wb') as f:
+            f.write(content)
+
+    def _load(self, datatype, key):
+        """Read a cached version from disk.
+
+        datatype : str
+            Datatype of the DOV object.
+        key : str
+            Unique and permanent object key of the DOV object.
+
+        Returns
+        -------
+        str (xml)
+            XML string of the DOV object, loaded from the cache.
+
+        """
+        filepath = self._get_filepath(datatype, key)
+        with gzip.open(filepath, 'rb') as f:
+            return f.read().decode('utf-8')


Test patch:
diff --git a/tests/test_encoding.py b/tests/test_encoding.py
index 2d17d81..9ecba5c 100644
--- a/tests/test_encoding.py
+++ b/tests/test_encoding.py
@@ -1,5 +1,6 @@
 # -*- encoding: utf-8 -*-
 import datetime
+import gzip
 import os
 
 import pandas as pd
@@ -37,7 +38,8 @@ from tests.test_search_itp_lithologischebeschrijvingen import (
 location_dov_xml = 'tests/data/encoding/invalidcharacters.xml'
 
 from tests.test_util_caching import (
-    cache,
+    plaintext_cache,
+    gziptext_cache,
     nocache,
 )
 
@@ -70,9 +72,10 @@ class TestEncoding(object):
 
     @pytest.mark.online
     @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
-    @pytest.mark.parametrize('cache', [[datetime.timedelta(minutes=15)]],
-                             indirect=['cache'])
-    def test_search_cache(self, cache):
+    @pytest.mark.parametrize('plaintext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['plaintext_cache'])
+    def test_search_plaintext_cache(self, plaintext_cache):
         """Test the search method with strange character in the output.
 
         Test whether the output has the correct encoding, both with and
@@ -80,8 +83,9 @@ class TestEncoding(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
 
         """
@@ -97,7 +101,7 @@ class TestEncoding(object):
         assert df.uitvoerder[0] == u'Societé Belge des Bétons'
 
         assert os.path.exists(os.path.join(
-            cache.cachedir, 'boring', '1928-031159.xml'))
+            plaintext_cache.cachedir, 'boring', '1928-031159.xml'))
 
         df = boringsearch.search(query=query,
                                  return_fields=('pkey_boring', 'uitvoerder',
@@ -107,27 +111,69 @@ class TestEncoding(object):
 
     @pytest.mark.online
     @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
-    @pytest.mark.parametrize('cache', [[datetime.timedelta(minutes=15)]],
-                             indirect=['cache'])
-    def test_caching(self, cache):
+    @pytest.mark.parametrize('gziptext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['gziptext_cache'])
+    def test_search_gziptext_cache(self, gziptext_cache):
+        """Test the search method with strange character in the output.
+
+        Test whether the output has the correct encoding, both with and
+        without using the cache.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+
+        """
+        boringsearch = BoringSearch()
+        query = PropertyIsEqualTo(
+            propertyname='pkey_boring',
+            literal='https://www.dov.vlaanderen.be/data/boring/1928-031159')
+
+        df = boringsearch.search(query=query,
+                                 return_fields=('pkey_boring', 'uitvoerder',
+                                                'mv_mtaw'))
+
+        assert df.uitvoerder[0] == u'Societé Belge des Bétons'
+
+        assert os.path.exists(os.path.join(
+            gziptext_cache.cachedir, 'boring', '1928-031159.xml.gz'))
+
+        df = boringsearch.search(query=query,
+                                 return_fields=('pkey_boring', 'uitvoerder',
+                                                'mv_mtaw'))
+
+        assert df.uitvoerder[0] == u'Societé Belge des Bétons'
+
+    @pytest.mark.online
+    @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
+    @pytest.mark.parametrize('plaintext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['plaintext_cache'])
+    def test_caching_plaintext(self, plaintext_cache):
         """Test the caching of an XML containing strange characters.
 
         Test whether the data is saved in the cache.
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '1995-056089.xml')
+            plaintext_cache.cachedir, 'boring', '1995-056089.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
         assert os.path.exists(cached_file)
 
         with open(cached_file, 'r', encoding='utf-8') as cf:
@@ -137,15 +183,57 @@ class TestEncoding(object):
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(0.5)
-        cache.get('https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
     @pytest.mark.online
     @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
-    @pytest.mark.parametrize('cache', [[datetime.timedelta(minutes=15)]],
-                             indirect=['cache'])
-    def test_save_content(self, cache):
+    @pytest.mark.parametrize('gziptext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['gziptext_cache'])
+    def test_caching_gziptext(self, gziptext_cache):
+        """Test the caching of an XML containing strange characters.
+
+        Test whether the data is saved in the cache.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '1995-056089.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        assert os.path.exists(cached_file)
+
+        with gzip.open(cached_file, 'rb') as cf:
+            cached_data = cf.read().decode('utf-8')
+            assert cached_data != ""
+
+        first_download_time = os.path.getmtime(cached_file)
+
+        time.sleep(0.5)
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        # assure we didn't redownload the file:
+        assert os.path.getmtime(cached_file) == first_download_time
+
+    @pytest.mark.online
+    @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
+    @pytest.mark.parametrize('plaintext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['plaintext_cache'])
+    def test_save_content_plaintext(self, plaintext_cache):
         """Test the caching of an XML containing strange characters.
 
         Test if the contents of the saved document are the same as the
@@ -153,18 +241,19 @@ class TestEncoding(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '1995-056089.xml')
+            plaintext_cache.cachedir, 'boring', '1995-056089.xml')
 
-        cache.remove()
+        plaintext_cache.remove()
         assert not os.path.exists(cached_file)
 
-        ref_data = cache.get(
+        ref_data = plaintext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
         assert os.path.exists(cached_file)
 
@@ -175,9 +264,78 @@ class TestEncoding(object):
 
     @pytest.mark.online
     @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
-    @pytest.mark.parametrize('cache', [[datetime.timedelta(minutes=15)]],
-                             indirect=['cache'])
-    def test_reuse_content(self, cache):
+    @pytest.mark.parametrize('gziptext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['gziptext_cache'])
+    def test_save_content_gziptext(self, gziptext_cache):
+        """Test the caching of an XML containing strange characters.
+
+        Test if the contents of the saved document are the same as the
+        original data.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '1995-056089.xml.gz')
+
+        gziptext_cache.remove()
+        assert not os.path.exists(cached_file)
+
+        ref_data = gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        assert os.path.exists(cached_file)
+
+        with gzip.open(cached_file, 'rb') as cached:
+            cached_data = cached.read()
+
+        assert cached_data == ref_data
+
+    @pytest.mark.online
+    @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
+    @pytest.mark.parametrize('plaintext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['plaintext_cache'])
+    def test_reuse_content_plaintext(self, plaintext_cache):
+        """Test the caching of an XML containing strange characters.
+
+        Test if the contents returned by the cache are the same as the
+        original data.
+
+        Parameters
+        ----------
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+
+        """
+        cached_file = os.path.join(
+            plaintext_cache.cachedir, 'boring', '1995-056089.xml')
+
+        plaintext_cache.remove()
+        assert not os.path.exists(cached_file)
+
+        ref_data = plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+        assert os.path.exists(cached_file)
+
+        cached_data = plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+
+        assert cached_data == ref_data
+
+    @pytest.mark.online
+    @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
+    @pytest.mark.parametrize('gziptext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['gziptext_cache'])
+    def test_reuse_content_gziptext(self, gziptext_cache):
         """Test the caching of an XML containing strange characters.
 
         Test if the contents returned by the cache are the same as the
@@ -185,22 +343,23 @@ class TestEncoding(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
             of 1 second.
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '1995-056089.xml')
+            gziptext_cache.cachedir, 'boring', '1995-056089.xml.gz')
 
-        cache.remove()
+        gziptext_cache.remove()
         assert not os.path.exists(cached_file)
 
-        ref_data = cache.get(
+        ref_data = gziptext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
         assert os.path.exists(cached_file)
 
-        cached_data = cache.get(
+        cached_data = gziptext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
 
         assert cached_data == ref_data
diff --git a/tests/test_util_caching.py b/tests/test_util_caching.py
index b4202cf..b13b748 100644
--- a/tests/test_util_caching.py
+++ b/tests/test_util_caching.py
@@ -1,5 +1,6 @@
 """Module grouping tests for the pydov.util.caching module."""
 import datetime
+import gzip
 import os
 import tempfile
 from io import open
@@ -9,7 +10,10 @@ import time
 import pytest
 
 import pydov
-from pydov.util.caching import TransparentCache
+from pydov.util.caching import (
+    PlainTextFileCache,
+    GzipTextFileCache,
+)
 
 
 @pytest.fixture
@@ -30,12 +34,12 @@ def mp_remote_xml(monkeypatch):
                 data = data.encode('utf-8')
         return data
 
-    monkeypatch.setattr(pydov.util.caching.TransparentCache,
+    monkeypatch.setattr(pydov.util.caching.AbstractFileCache,
                         '_get_remote', _get_remote_data)
 
 
 @pytest.fixture
-def cache(request):
+def plaintext_cache(request):
     """Fixture for a temporary cache.
 
     This fixture should be parametrized, with a list of parameters in the
@@ -54,14 +58,45 @@ def cache(request):
     else:
         max_age = request.param[0]
 
-    transparent_cache = TransparentCache(
+    plaintext_cache = PlainTextFileCache(
         cachedir=os.path.join(tempfile.gettempdir(), 'pydov_tests'),
         max_age=max_age)
-    pydov.cache = transparent_cache
+    pydov.cache = plaintext_cache
 
-    yield transparent_cache
+    yield plaintext_cache
 
-    transparent_cache.remove()
+    plaintext_cache.remove()
+    pydov.cache = orig_cache
+
+
+@pytest.fixture
+def gziptext_cache(request):
+    """Fixture for a temporary cache.
+
+    This fixture should be parametrized, with a list of parameters in the
+    order described below.
+
+    Paramaters
+    ----------
+    max_age : datetime.timedelta
+        The maximum age to use for the cache.
+
+    """
+    orig_cache = pydov.cache
+
+    if len(request.param) == 0:
+        max_age = datetime.timedelta(seconds=1)
+    else:
+        max_age = request.param[0]
+
+    gziptext_cache = GzipTextFileCache(
+        cachedir=os.path.join(tempfile.gettempdir(), 'pydov_tests'),
+        max_age=max_age)
+    pydov.cache = gziptext_cache
+
+    yield gziptext_cache
+
+    gziptext_cache.remove()
     pydov.cache = orig_cache
 
 
@@ -74,12 +109,13 @@ def nocache():
     pydov.cache = orig_cache
 
 
-class TestTransparentCache(object):
-    """Class grouping tests for the pydov.util.caching.TransparentCache
+class TestPlainTextFileCacheCache(object):
+    """Class grouping tests for the pydov.util.caching.PlainTextFileCache
     class."""
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_clean(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_clean(self, plaintext_cache, mp_remote_xml):
         """Test the clean method.
 
         Test whether the cached file and the cache directory are nonexistent
@@ -87,8 +123,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -96,22 +133,24 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
-        cache.clean()
+        plaintext_cache.clean()
         assert os.path.exists(cached_file)
-        assert os.path.exists(cache.cachedir)
+        assert os.path.exists(plaintext_cache.cachedir)
 
         time.sleep(1.5)
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
-        assert os.path.exists(cache.cachedir)
+        assert os.path.exists(plaintext_cache.cachedir)
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_remove(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_remove(self, plaintext_cache, mp_remote_xml):
         """Test the remove method.
 
         Test whether the cache directory is nonexistent after the remove
@@ -119,8 +158,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -128,25 +168,28 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
-        cache.remove()
+        plaintext_cache.remove()
         assert not os.path.exists(cached_file)
-        assert not os.path.exists(cache.cachedir)
+        assert not os.path.exists(plaintext_cache.cachedir)
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_get_save(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_get_save(self, plaintext_cache, mp_remote_xml):
         """Test the get method.
 
         Test whether the document is saved in the cache.
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -154,16 +197,18 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_get_reuse(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_get_reuse(self, plaintext_cache, mp_remote_xml):
         """Test the get method.
 
         Test whether the document is saved in the cache and reused in a
@@ -171,8 +216,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -180,23 +226,26 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(0.5)
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_get_invalid(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_get_invalid(self, plaintext_cache, mp_remote_xml):
         """Test the get method.
 
         Test whether the document is saved in the cache not reused if the
@@ -204,8 +253,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -213,23 +263,26 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(1.5)
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         # assure we did redownload the file, since original is invalid now:
         assert os.path.getmtime(cached_file) > first_download_time
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_save_content(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_save_content(self, plaintext_cache, mp_remote_xml):
         """Test whether the data is saved in the cache.
 
         Test if the contents of the saved document are the same as the
@@ -237,8 +290,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -246,12 +300,13 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r',
@@ -263,8 +318,9 @@ class TestTransparentCache(object):
 
         assert cached_data == ref_data
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_reuse_content(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_reuse_content(self, plaintext_cache, mp_remote_xml):
         """Test whether the saved data is reused.
 
         Test if the contents returned by the cache are the same as the
@@ -272,8 +328,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -281,24 +338,308 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
-        cache.clean()
+        plaintext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        cache.get('https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        with open('tests/data/types/boring/boring.xml', 'r') as ref:
+            ref_data = ref.read().encode('utf-8')
+
+        cached_data = plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+
+        assert cached_data == ref_data
+
+    @pytest.mark.parametrize('plaintext_cache', [[]],
+                             indirect=['plaintext_cache'])
+    def test_return_type(self, plaintext_cache, mp_remote_xml):
+        """The the return type of the get method.
+
+        Test wether the get method returns the data in the same datatype (
+        i.e. bytes) regardless of the data was cached or not.
+
+        Parameters
+        ----------
+        plaintext_cache : pytest.fixture providing
+                pydov.util.caching.PlainTextFileCache
+            PlainTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            plaintext_cache.cachedir, 'boring', '2004-103984.xml')
+
+        plaintext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        ref_data = plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert type(ref_data) is bytes
+
+        assert os.path.exists(cached_file)
+
+        cached_data = plaintext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert type(cached_data) is bytes
+
+
+class TestGzipTextFileCacheCache(object):
+    """Class grouping tests for the pydov.util.caching.PlainTextFileCache
+    class."""
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_clean(self, gziptext_cache, mp_remote_xml):
+        """Test the clean method.
+
+        Test whether the cached file and the cache directory are nonexistent
+        after the clean method has been called.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        gziptext_cache.clean()
+        assert os.path.exists(cached_file)
+        assert os.path.exists(gziptext_cache.cachedir)
+
+        time.sleep(1.5)
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+        assert os.path.exists(gziptext_cache.cachedir)
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_remove(self, gziptext_cache, mp_remote_xml):
+        """Test the remove method.
+
+        Test whether the cache directory is nonexistent after the remove
+        method has been called.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        gziptext_cache.remove()
+        assert not os.path.exists(cached_file)
+        assert not os.path.exists(gziptext_cache.cachedir)
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_get_save(self, gziptext_cache, mp_remote_xml):
+        """Test the get method.
+
+        Test whether the document is saved in the cache.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_get_reuse(self, gziptext_cache, mp_remote_xml):
+        """Test the get method.
+
+        Test whether the document is saved in the cache and reused in a
+        second function call.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        first_download_time = os.path.getmtime(cached_file)
+
+        time.sleep(0.5)
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        # assure we didn't redownload the file:
+        assert os.path.getmtime(cached_file) == first_download_time
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_get_invalid(self, gziptext_cache, mp_remote_xml):
+        """Test the get method.
+
+        Test whether the document is saved in the cache not reused if the
+        second function call is after the maximum age of the cached file.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        first_download_time = os.path.getmtime(cached_file)
+
+        time.sleep(1.5)
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        # assure we did redownload the file, since original is invalid now:
+        assert os.path.getmtime(cached_file) > first_download_time
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_save_content(self, gziptext_cache, mp_remote_xml):
+        """Test whether the data is saved in the cache.
+
+        Test if the contents of the saved document are the same as the
+        original data.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+        assert os.path.exists(cached_file)
+
+        with open('tests/data/types/boring/boring.xml', 'r',
+                  encoding='utf-8') as ref:
+            ref_data = ref.read()
+
+        with gzip.open(cached_file, 'rb') as cached:
+            cached_data = cached.read().decode('utf-8')
+
+        assert cached_data == ref_data
+
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_reuse_content(self, gziptext_cache, mp_remote_xml):
+        """Test whether the saved data is reused.
+
+        Test if the contents returned by the cache are the same as the
+        original data.
+
+        Parameters
+        ----------
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
+            of 1 second.
+        mp_remote_xml : pytest.fixture
+            Monkeypatch the call to the remote DOV service returning an XML
+            document.
+
+        """
+        cached_file = os.path.join(
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
+
+        gziptext_cache.clean()
+        assert not os.path.exists(cached_file)
+
+        gziptext_cache.get(
+            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r') as ref:
             ref_data = ref.read().encode('utf-8')
 
-        cached_data = cache.get(
+        cached_data = gziptext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
 
         assert cached_data == ref_data
 
-    @pytest.mark.parametrize('cache', [[]], indirect=['cache'])
-    def test_return_type(self, cache, mp_remote_xml):
+    @pytest.mark.parametrize('gziptext_cache', [[]],
+                             indirect=['gziptext_cache'])
+    def test_return_type(self, gziptext_cache, mp_remote_xml):
         """The the return type of the get method.
 
         Test wether the get method returns the data in the same datatype (
@@ -306,8 +647,9 @@ class TestTransparentCache(object):
 
         Parameters
         ----------
-        cache : pytest.fixture providing  pydov.util.caching.TransparentCache
-            TransparentCache using a temporary directory and a maximum age
+        gziptext_cache : pytest.fixture providing
+                pydov.util.caching.GzipTextFileCache
+            GzipTextFileCache using a temporary directory and a maximum age
             of 1 second.
         mp_remote_xml : pytest.fixture
             Monkeypatch the call to the remote DOV service returning an XML
@@ -315,17 +657,17 @@ class TestTransparentCache(object):
 
         """
         cached_file = os.path.join(
-            cache.cachedir, 'boring', '2004-103984.xml')
+            gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
 
-        cache.clean()
+        gziptext_cache.clean()
         assert not os.path.exists(cached_file)
 
-        ref_data = cache.get(
+        ref_data = gziptext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert type(ref_data) is bytes
 
         assert os.path.exists(cached_file)
 
-        cached_data = cache.get(
+        cached_data = gziptext_cache.get(
             'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
         assert type(cached_data) is bytes
diff --git a/tests/test_util_hooks.py b/tests/test_util_hooks.py
index c1499c2..a40247e 100644
--- a/tests/test_util_hooks.py
+++ b/tests/test_util_hooks.py
@@ -11,7 +11,7 @@ from pydov.util.hooks import (
 from tests.abstract import service_ok
 
 from tests.test_util_caching import (
-    cache,
+    plaintext_cache,
     nocache,
 )
 
@@ -174,9 +174,10 @@ class TestHooks(object):
 
     @pytest.mark.online
     @pytest.mark.skipif(not service_ok(), reason="DOV service is unreachable")
-    @pytest.mark.parametrize('cache', [[datetime.timedelta(minutes=15)]],
-                             indirect=['cache'])
-    def test_wfs_and_xml_cache(self, temp_hooks, cache):
+    @pytest.mark.parametrize('plaintext_cache',
+                             [[datetime.timedelta(minutes=15)]],
+                             indirect=['plaintext_cache'])
+    def test_wfs_and_xml_cache(self, temp_hooks, plaintext_cache):
         """Test the search method providing both a location and a query.
 
         Test whether a dataframe is returned.
@@ -190,7 +191,7 @@ class TestHooks(object):
             Monkeypatch the call to get WFS features.
         temp_hooks : pytest.fixture
             Fixture removing default hooks and installing HookCounter.
-        cache : pytest.fixture
+        plaintext_cache : pytest.fixture
             Fixture temporarily setting up a testcache with max_age of 1
             second.