Repository: DOV-Vlaanderen/pydov

Issue:
Add support for pluggable types
Pluggable types allow users to customize and extend existing pydov types and use them in search classes. This enables advanced customisation of (default) dataframe outputs and allows users to add custom XML fields not included in the default datatypes and -frames.

API wise, I think this can be added by introducing an optional parameter specifying the objecttype class when creating a search object. This wouldn't have impact on existing use-cases.

For example, this would allow to add 'filter_binnendiameter' and 'opmeter' to GrondwaterFilterSearch (proof-of-concept):

```python
class MyPeilmeting(Peilmeting):

    fields = Peilmeting.extend_fields([
        XmlField(name='opmeter',
                 source_xpath='/opmeter/naam',
                 definition='Naam van de opmeter',
                 datatype='string',
                 notnull=False)
    ])


class MyGrondwaterFilter(GrondwaterFilter):

    subtypes = [MyPeilmeting]

    fields = GrondwaterFilter.extend_fields([
        XmlField(name='filter_binnendiameter',
                 source_xpath='/filter/opbouw/onderdeel
                              '[filterelement="filter"]/'
                              'binnendiameter',
                 definition='Binnendiameter van de filter.',
                 datatype='integer',
                 notnull=False)
    ])


fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilter)
fields = fs.get_fields()

df_filters = fs.search(
    query=PropertyIsEqualTo(
        'pkey_filter',
        'https://www.dov.vlaanderen.be/data/filter/2003-001357')
)

print(df_filter.iloc[0].transpose())
```
```text
pkey_filter                https://www.dov.vlaanderen.be/data/filter/2003...
pkey_grondwaterlocatie     https://www.dov.vlaanderen.be/data/put/2017-00...
gw_id                                                              050/21/5a
filternummer                                                               1
filtertype                                                        peilfilter
x                                                                      66715
y                                                                     215715
mv_mtaw                                                                    3
gemeente                                                          Zuienkerke
meetnet_code                                                               8
aquifer_code                                                            0100
grondwaterlichaam_code                                        KPS_0160_GWL_1
regime                                                             freatisch
diepte_onderkant_filter                                                  2.5
lengte_filter                                                            0.5
filter_binnendiameter                                                     58
datum                                                             2004-04-26
tijdstip                                                                 NaN
peil_mtaw                                                               2.36
betrouwbaarheid                                                         goed
methode                                                             peillint
filterstatus                                                         in rust
filtertoestand                                                             1
opmeter                                                                 Labo
````

Progress can be tracked in the `pluggable-types` branch.

Discussion:

Patch:
diff --git a/docs/df_format.rst b/docs/df_format.rst
index 1ccd8a8..ce02573 100644
--- a/docs/df_format.rst
+++ b/docs/df_format.rst
@@ -1,8 +1,8 @@
 .. _object_types:
 
-=======================
-Output data description
-=======================
+=========================
+# Output data description
+=========================
 
 .. warning::
 
diff --git a/docs/index.rst b/docs/index.rst
index a3ab656..e58b31b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -82,8 +82,8 @@ Contents:
 
    query_attribute
    query_location
+   output_fields
    performance
-   df_format
    caching
    hooks
 
@@ -97,6 +97,7 @@ Contents:
    history
    authors
    conduct
+   df_format
 
 Indices and tables
 ==================
diff --git a/docs/notebooks/customizing_object_types.ipynb b/docs/notebooks/customizing_object_types.ipynb
new file mode 100644
index 0000000..cf47dd0
--- /dev/null
+++ b/docs/notebooks/customizing_object_types.ipynb
@@ -0,0 +1,1227 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Examples of object type customization"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Listing techniques per CPT measurement"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "%matplotlib inline"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "While performing CPT measurements, different techniques can be used. Since these can have an impact on the results, it can be interesting to download this additional information in order to better comprehend the CPT data.\n",
+    "\n",
+    "Different CPT techniques can be applied at various depths, so the easiest way to add this to pydov is to use a new Sondering subtype `Techniek`, as shown below. The result will be that one can then choose to query CPT measurements and either retrieve a dataframe with the measurements themselves, or a dataframe with the techniques applied. The user can subsequently compare or merge the two dataframes at will."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.types.fields import XmlField, XsdType\n",
+    "from pydov.types.abstract import AbstractDovSubType\n",
+    "from pydov.types.sondering import Sondering"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A new subtype has to be a subclass of the AbstractDovSubType class and implement two class variables: rootpath and fields.\n",
+    "\n",
+    "The `rootpath` is the XML XPath expression matching all instances of this subtype in the XML. One instance of this subtype will be created for each element matched by the `rootpath` XPath expression.\n",
+    "\n",
+    "In the `fields` all the fields of this subtype are listed. These are instances of `XmlField` and should have at minimum a `name`, a `source_xpath` and a `datatype`. Additionally, a field should have a `definition` and can have a reference to an XSD schema type. The latter will be resolved and parsed at runtime, resulting in a list of values of this field."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "class Techniek(AbstractDovSubType):\n",
+    "    \n",
+    "    rootpath = './/sondering/sondeonderzoek/penetratietest/technieken'\n",
+    "\n",
+    "    fields = [\n",
+    "        XmlField(name='techniek_diepte_van',\n",
+    "                 source_xpath='/diepte_van',\n",
+    "                 definition='Enkel van toepassing voor het plaatsen van voerbuizen - '\n",
+    "                            '(code V) of het boren door een harde laag (code B).',\n",
+    "                 datatype='float'),\n",
+    "        XmlField(name='techniek_diepte',\n",
+    "                 source_xpath='/diepte_techniek',\n",
+    "                 definition='Diepte waarop techniek toegepast werd.',\n",
+    "                 datatype='float'),\n",
+    "        XmlField(name='techniek',\n",
+    "                 source_xpath='/techniek',\n",
+    "                 definition='De gebruikte techniek.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/sondering/SonderingDataCodes.xsd',\n",
+    "                     typename='SondeerTechniekEnumType')),\n",
+    "        XmlField(name='techniek_andere',\n",
+    "                 source_xpath='/techniek_andere',\n",
+    "                 definition=\"De gebruikte techniek (enkel van toepassing indien de techniek = 'andere').\",\n",
+    "                 datatype='string')\n",
+    "    ]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In order to be able to use this subtype in a search query, we have to create a subclass of the original main type (Sondering) and register our new subtype:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "class SonderingTechnieken(Sondering):\n",
+    "    subtypes = [Techniek]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The next step is to instantiate the `SonderingSearch` class with our newly created type:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.search.sondering import SonderingSearch\n",
+    "\n",
+    "cpts = SonderingSearch(objecttype=SonderingTechnieken)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If everything worked out, you should be able to see the new fields in the `get_fields` output:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'cost': 10,\n",
+       " 'definition': 'De gebruikte techniek.',\n",
+       " 'name': 'techniek',\n",
+       " 'notnull': False,\n",
+       " 'query': False,\n",
+       " 'type': 'string',\n",
+       " 'values': {'B': 'sondeerbuizen door een harde laag geduwd of geboord',\n",
+       "  'E': 'sondeerbuizen op en neer bewogen',\n",
+       "  'S': 'uitvoering sondering tijdelijk onderbroken',\n",
+       "  'V': 'plaatsing van voerbuizen',\n",
+       "  'andere': 'een andere dan de standaard voorziene technieken'}}"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cpts.get_fields()['techniek']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Quering is exactly the same as with the default Sondering type:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[000/067] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[050/067] ccccccccccccccccc\n"
+     ]
+    }
+   ],
+   "source": [
+    "from pydov.util.location import WithinDistance, Point\n",
+    "\n",
+    "df = cpts.search(location=WithinDistance(Point(150000, 150000), 10000, 'meter'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "One can use the values from the XSD type to add a human-readably column with the different techniques:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "df['techniek_label'] = df['techniek'].map(cpts.get_fields()['techniek']['values'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>pkey_sondering</th>\n",
+       "      <th>sondeernummer</th>\n",
+       "      <th>x</th>\n",
+       "      <th>y</th>\n",
+       "      <th>start_sondering_mtaw</th>\n",
+       "      <th>diepte_sondering_van</th>\n",
+       "      <th>diepte_sondering_tot</th>\n",
+       "      <th>datum_aanvang</th>\n",
+       "      <th>uitvoerder</th>\n",
+       "      <th>sondeermethode</th>\n",
+       "      <th>apparaat</th>\n",
+       "      <th>datum_gw_meting</th>\n",
+       "      <th>diepte_gw_m</th>\n",
+       "      <th>techniek_diepte_van</th>\n",
+       "      <th>techniek_diepte</th>\n",
+       "      <th>techniek</th>\n",
+       "      <th>techniek_andere</th>\n",
+       "      <th>techniek_label</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/sondering/1...</td>\n",
+       "      <td>GEO-79/291-SII</td>\n",
+       "      <td>147245.0</td>\n",
+       "      <td>158407.0</td>\n",
+       "      <td>67.50</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>32.2</td>\n",
+       "      <td>1979-08-20</td>\n",
+       "      <td>Rijksinstituut voor Grondmechanica</td>\n",
+       "      <td>discontinu mechanisch</td>\n",
+       "      <td>200KN</td>\n",
+       "      <td>NaT</td>\n",
+       "      <td>1.95</td>\n",
+       "      <td>7.4</td>\n",
+       "      <td>7.4</td>\n",
+       "      <td>V</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>plaatsing van voerbuizen</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/sondering/1...</td>\n",
+       "      <td>GEO-79/291-SI</td>\n",
+       "      <td>147231.0</td>\n",
+       "      <td>158421.0</td>\n",
+       "      <td>67.60</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>32.6</td>\n",
+       "      <td>1979-08-17</td>\n",
+       "      <td>Rijksinstituut voor Grondmechanica</td>\n",
+       "      <td>discontinu mechanisch</td>\n",
+       "      <td>200KN</td>\n",
+       "      <td>NaT</td>\n",
+       "      <td>2.10</td>\n",
+       "      <td>6.4</td>\n",
+       "      <td>6.4</td>\n",
+       "      <td>V</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>plaatsing van voerbuizen</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/sondering/1...</td>\n",
+       "      <td>GEO-79/291-SIII</td>\n",
+       "      <td>147241.0</td>\n",
+       "      <td>158388.0</td>\n",
+       "      <td>69.40</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>33.2</td>\n",
+       "      <td>1979-08-20</td>\n",
+       "      <td>Rijksinstituut voor Grondmechanica</td>\n",
+       "      <td>discontinu mechanisch</td>\n",
+       "      <td>200KN</td>\n",
+       "      <td>NaT</td>\n",
+       "      <td>2.88</td>\n",
+       "      <td>6.4</td>\n",
+       "      <td>6.4</td>\n",
+       "      <td>V</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>plaatsing van voerbuizen</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/sondering/1...</td>\n",
+       "      <td>GEO-79/199-SIV</td>\n",
+       "      <td>145564.0</td>\n",
+       "      <td>149739.0</td>\n",
+       "      <td>126.06</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>8.2</td>\n",
+       "      <td>1970-06-26</td>\n",
+       "      <td>Rijksinstituut voor Grondmechanica</td>\n",
+       "      <td>discontinu mechanisch</td>\n",
+       "      <td>100KN</td>\n",
+       "      <td>NaT</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/sondering/1...</td>\n",
+       "      <td>GEO-79/199-SV</td>\n",
+       "      <td>145546.0</td>\n",
+       "      <td>149746.0</td>\n",
+       "      <td>126.03</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>10.2</td>\n",
+       "      <td>1979-06-25</td>\n",
+       "      <td>Rijksinstituut voor Grondmechanica</td>\n",
+       "      <td>discontinu mechanisch</td>\n",
+       "      <td>100KN</td>\n",
+       "      <td>NaT</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                      pkey_sondering    sondeernummer  \\\n",
+       "0  https://www.dov.vlaanderen.be/data/sondering/1...   GEO-79/291-SII   \n",
+       "1  https://www.dov.vlaanderen.be/data/sondering/1...    GEO-79/291-SI   \n",
+       "2  https://www.dov.vlaanderen.be/data/sondering/1...  GEO-79/291-SIII   \n",
+       "3  https://www.dov.vlaanderen.be/data/sondering/1...   GEO-79/199-SIV   \n",
+       "4  https://www.dov.vlaanderen.be/data/sondering/1...    GEO-79/199-SV   \n",
+       "\n",
+       "          x         y  start_sondering_mtaw  diepte_sondering_van  \\\n",
+       "0  147245.0  158407.0                 67.50                   0.0   \n",
+       "1  147231.0  158421.0                 67.60                   0.0   \n",
+       "2  147241.0  158388.0                 69.40                   0.0   \n",
+       "3  145564.0  149739.0                126.06                   0.0   \n",
+       "4  145546.0  149746.0                126.03                   0.0   \n",
+       "\n",
+       "   diepte_sondering_tot datum_aanvang                          uitvoerder  \\\n",
+       "0                  32.2    1979-08-20  Rijksinstituut voor Grondmechanica   \n",
+       "1                  32.6    1979-08-17  Rijksinstituut voor Grondmechanica   \n",
+       "2                  33.2    1979-08-20  Rijksinstituut voor Grondmechanica   \n",
+       "3                   8.2    1970-06-26  Rijksinstituut voor Grondmechanica   \n",
+       "4                  10.2    1979-06-25  Rijksinstituut voor Grondmechanica   \n",
+       "\n",
+       "          sondeermethode apparaat datum_gw_meting  diepte_gw_m  \\\n",
+       "0  discontinu mechanisch    200KN             NaT         1.95   \n",
+       "1  discontinu mechanisch    200KN             NaT         2.10   \n",
+       "2  discontinu mechanisch    200KN             NaT         2.88   \n",
+       "3  discontinu mechanisch    100KN             NaT          NaN   \n",
+       "4  discontinu mechanisch    100KN             NaT          NaN   \n",
+       "\n",
+       "   techniek_diepte_van  techniek_diepte techniek  techniek_andere  \\\n",
+       "0                  7.4              7.4        V              NaN   \n",
+       "1                  6.4              6.4        V              NaN   \n",
+       "2                  6.4              6.4        V              NaN   \n",
+       "3                  NaN              NaN      NaN              NaN   \n",
+       "4                  NaN              NaN      NaN              NaN   \n",
+       "\n",
+       "             techniek_label  \n",
+       "0  plaatsing van voerbuizen  \n",
+       "1  plaatsing van voerbuizen  \n",
+       "2  plaatsing van voerbuizen  \n",
+       "3                       NaN  \n",
+       "4                       NaN  "
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df.head()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Adding location and height details to Boring dataframe"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There is more to the location of a borehole than meets the eye! The default dataframe lists already multiple fields regarding the location of the borehole, both planimetric as altimetric:\n",
+    "\n",
+    "* `x` and `y` are the planimetric coordinates of the borehole\n",
+    "* `start_boring_mtaw` is the height of the start (aanvangspeil) of the borehole\n",
+    "* `mv_mtaw` is the height of the ground level at the time of the making of the borehole\n",
+    "\n",
+    "However, we have more information available regarding the (origin of) these coordinates. Each of them has an associated method (methode) and reliability (betrouwbaarheid).\n",
+    "\n",
+    "We also make the distinction between the height of the ground level (maaiveld) and the height of the start of the borehole (aanvangspeil). If the borehole was started at ground level both are the same, but this is not necessarily the case. Furthermore the height of the start of the borehole can be either absolute (measured individually) or relative to the ground level.\n",
+    "\n",
+    "If we want to have all this extra information available when retrieving the borehole dataframe output (or that of another DOV type), we can add the extra XML fields in a subclass of the Boring type:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.types.fields import XmlField, XsdType\n",
+    "from pydov.types.boring import Boring\n",
+    "\n",
+    "class BoringMethodeXyz(Boring):\n",
+    "    \n",
+    "    __generiekeDataCodes = 'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/generiek/GeneriekeDataCodes.xsd'\n",
+    "    \n",
+    "    fields = Boring.extend_fields([\n",
+    "        XmlField(name='methode_xy',\n",
+    "                 source_xpath='/boring/xy/methode_opmeten',\n",
+    "                 definition='Methode waarop de x en y-coordinaat opgemeten werden.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='MethodeOpmetenXyEnumType')),\n",
+    "        XmlField(name='betrouwbaarheid_xy',\n",
+    "                 source_xpath='/boring/xy/betrouwbaarheid',\n",
+    "                 definition='Betrouwbaarheid van het opmeten van de x en y-coordinaat.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='BetrouwbaarheidXyzEnumType')),\n",
+    "        XmlField(name='methode_mv',\n",
+    "                 source_xpath='/boring/oorspronkelijk_maaiveld/methode_opmeten',\n",
+    "                 definition='Methode waarop de Z-coördinaat van het maaiveld opgemeten werd.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='MethodeOpmetenZEnumType')),\n",
+    "        XmlField(name='betrouwbaarheid_mv',\n",
+    "                 source_xpath='/boring/oorspronkelijk_maaiveld/betrouwbaarheid',\n",
+    "                 definition='Betrouwbaarheid van het opmeten van de z-coordinaat van het maaiveld.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='BetrouwbaarheidXyzEnumType')),\n",
+    "        XmlField(name='aanvangspeil_mtaw',\n",
+    "                 source_xpath='/boring/aanvangspeil/waarde',\n",
+    "                 definition='Hoogte in mTAW van het startpunt van de boring (boortafel, bouwput etc).',\n",
+    "                 datatype='float'),\n",
+    "        XmlField(name='methode_aanvangspeil',\n",
+    "                 source_xpath='/boring/aanvangspeil/methode_opmeten',\n",
+    "                 definition='Methode waarop de Z-coördinaat van het aanvangspeil opgemeten werd.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='MethodeOpmetenZEnumType')),\n",
+    "        XmlField(name='betrouwbaarheid_aanvangspeil',\n",
+    "                 source_xpath='/boring/aanvangspeil/betrouwbaarheid',\n",
+    "                 definition='Betrouwbaarheid van het opmeten van de z-coordinaat van het aanvangspeil.',\n",
+    "                 datatype='string',\n",
+    "                 xsd_type=XsdType(\n",
+    "                     xsd_schema=__generiekeDataCodes,\n",
+    "                     typename='MethodeOpmetenZEnumType')),\n",
+    "    ])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "When instantiating our BoringSearch object, we now explicitly set our new type as objecttype to search:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.search.boring import BoringSearch\n",
+    "\n",
+    "bs = BoringSearch(objecttype=BoringMethodeXyz)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'cost': 10,\n",
+       " 'definition': 'Maaiveldhoogte in mTAW op dag dat de boring uitgevoerd werd.',\n",
+       " 'name': 'mv_mtaw',\n",
+       " 'notnull': False,\n",
+       " 'query': False,\n",
+       " 'type': 'float'}"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "bs.get_fields()['mv_mtaw']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Searching for boreholes remains exactly the same, but will reveal the extra information in the output dataframe:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[000/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[050/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[100/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[150/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[200/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[250/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[300/361] cccccccccccccccccccccccccccccccccccccccccccccccccc\n",
+      "[350/361] ccccccccccc\n"
+     ]
+    }
+   ],
+   "source": [
+    "from pydov.util.location import WithinDistance, Point\n",
+    "\n",
+    "df = bs.search(location=WithinDistance(Point(150000, 150000), 10000, 'meter'),\n",
+    "               return_fields=('pkey_boring', 'boornummer', 'x', 'y', 'methode_xy', 'betrouwbaarheid_xy', \n",
+    "                              'mv_mtaw', 'methode_mv', 'betrouwbaarheid_mv', 'aanvangspeil_mtaw', \n",
+    "                              'methode_aanvangspeil', 'betrouwbaarheid_aanvangspeil', 'start_boring_mtaw'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>pkey_boring</th>\n",
+       "      <th>boornummer</th>\n",
+       "      <th>x</th>\n",
+       "      <th>y</th>\n",
+       "      <th>mv_mtaw</th>\n",
+       "      <th>start_boring_mtaw</th>\n",
+       "      <th>methode_xy</th>\n",
+       "      <th>betrouwbaarheid_xy</th>\n",
+       "      <th>methode_mv</th>\n",
+       "      <th>betrouwbaarheid_mv</th>\n",
+       "      <th>aanvangspeil_mtaw</th>\n",
+       "      <th>methode_aanvangspeil</th>\n",
+       "      <th>betrouwbaarheid_aanvangspeil</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/boring/1891...</td>\n",
+       "      <td>BGD115E0018C.2</td>\n",
+       "      <td>145692.0</td>\n",
+       "      <td>157605.0</td>\n",
+       "      <td>55.0</td>\n",
+       "      <td>55.0</td>\n",
+       "      <td>gedigitaliseerd op topokaart</td>\n",
+       "      <td>twijfelachtig</td>\n",
+       "      <td>afgeleid van topokaart</td>\n",
+       "      <td>twijfelachtig</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/boring/1894...</td>\n",
+       "      <td>vgmperceel6-B2</td>\n",
+       "      <td>140857.0</td>\n",
+       "      <td>151875.0</td>\n",
+       "      <td>54.0</td>\n",
+       "      <td>54.0</td>\n",
+       "      <td>gedigitaliseerd op topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>afgeleid van topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/boring/1913...</td>\n",
+       "      <td>vgmperceel6-B4</td>\n",
+       "      <td>140236.0</td>\n",
+       "      <td>150691.0</td>\n",
+       "      <td>85.0</td>\n",
+       "      <td>85.0</td>\n",
+       "      <td>gedigitaliseerd op topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>afgeleid van topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/boring/1927...</td>\n",
+       "      <td>vgmperceel6-B9</td>\n",
+       "      <td>142139.0</td>\n",
+       "      <td>151678.0</td>\n",
+       "      <td>75.0</td>\n",
+       "      <td>75.0</td>\n",
+       "      <td>gedigitaliseerd op topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>afgeleid van topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/boring/1927...</td>\n",
+       "      <td>vgmperceel6-B10</td>\n",
+       "      <td>144692.0</td>\n",
+       "      <td>152764.0</td>\n",
+       "      <td>93.0</td>\n",
+       "      <td>93.0</td>\n",
+       "      <td>gedigitaliseerd op topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>afgeleid van topokaart</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                         pkey_boring       boornummer  \\\n",
+       "0  https://www.dov.vlaanderen.be/data/boring/1891...   BGD115E0018C.2   \n",
+       "1  https://www.dov.vlaanderen.be/data/boring/1894...   vgmperceel6-B2   \n",
+       "2  https://www.dov.vlaanderen.be/data/boring/1913...   vgmperceel6-B4   \n",
+       "3  https://www.dov.vlaanderen.be/data/boring/1927...   vgmperceel6-B9   \n",
+       "4  https://www.dov.vlaanderen.be/data/boring/1927...  vgmperceel6-B10   \n",
+       "\n",
+       "          x         y  mv_mtaw  start_boring_mtaw  \\\n",
+       "0  145692.0  157605.0     55.0               55.0   \n",
+       "1  140857.0  151875.0     54.0               54.0   \n",
+       "2  140236.0  150691.0     85.0               85.0   \n",
+       "3  142139.0  151678.0     75.0               75.0   \n",
+       "4  144692.0  152764.0     93.0               93.0   \n",
+       "\n",
+       "                     methode_xy betrouwbaarheid_xy              methode_mv  \\\n",
+       "0  gedigitaliseerd op topokaart      twijfelachtig  afgeleid van topokaart   \n",
+       "1  gedigitaliseerd op topokaart           onbekend  afgeleid van topokaart   \n",
+       "2  gedigitaliseerd op topokaart           onbekend  afgeleid van topokaart   \n",
+       "3  gedigitaliseerd op topokaart           onbekend  afgeleid van topokaart   \n",
+       "4  gedigitaliseerd op topokaart           onbekend  afgeleid van topokaart   \n",
+       "\n",
+       "  betrouwbaarheid_mv  aanvangspeil_mtaw methode_aanvangspeil  \\\n",
+       "0      twijfelachtig                NaN                  NaN   \n",
+       "1           onbekend                NaN                  NaN   \n",
+       "2           onbekend                NaN                  NaN   \n",
+       "3           onbekend                NaN                  NaN   \n",
+       "4           onbekend                NaN                  NaN   \n",
+       "\n",
+       "  betrouwbaarheid_aanvangspeil  \n",
+       "0                          NaN  \n",
+       "1                          NaN  \n",
+       "2                          NaN  \n",
+       "3                          NaN  \n",
+       "4                          NaN  "
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df.head()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<matplotlib.legend.Legend at 0x13469b70>"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAADuCAYAAAAQqxqwAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4wLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvpW3flQAAIABJREFUeJzt3XlcVNX7B/DPubPAwLCIbAKyyOIM\n4IpCqLiWSy4l7mJmpblk5Zp908rSLL+llZVpaqVm7maZ3yzN3BMVUbYZwQVxFxXZhmVm7vn9MQM/\nRLZB4MLMeb9e80LGmXueGeaZc++59zmHUErBMIx544QOgGGY+scSnWEsAEt0hrEALNEZxgKwRGcY\nC8ASnWEsAEt0hrEALNEZxgKwRGcYC8ASnWEsAEt0hrEALNEZxgKwRGcYC8ASnWEsAEt0hrEALNEZ\nxgKwRGcYC8ASnWEsAEt0hrEALNEZxgKwRGcYC8ASnWEsAEt0hrEALNEZxgKwRGcYC8ASnWEsAEt0\nhrEALNEZxgKwRGcYC8ASnWEsgFjoAJiGo1IobQE4lrk5GH/aAdADKABQWMFPDYDbSrUqW4CwmTpA\n2Pro5kOlUMoABBtvgQACjD99ADTDk3+xZwG4UuaWXubfF5VqlfYJt8/UE5boTZRKoSQA2gPoBaAr\ngDYA/CHc4VgRgHMATgE4DeC4Uq26LFAsTDks0ZsQlUIZDENi9wbQA0BzYSOq1g0AR4y3fUq1Kl3Y\ncCwXS/RGTKVQSgEMBDAchuR2FzaiJxYHYDuAHUq16pLQwVgSluiNjHGXvCuAFwCMgOHY2hydA7AD\nwHalWpUqdDDmjiV6I6FSKINgSO4YAH4Ch9PQEgGsBfC9Uq3KEzoYc8QSXUAqhZIDMBTAHABPCRxO\nY5AN4HsAK9jxfN1iiS4AlUIpATAOwDwArQUOpzHSA/gVwBdKteqo0MGYA5boDUilUNoAeBXALAAt\nBQ6nqYgD8BmArUq1in1Ya4klegNQKZT2AN4E8AYAZ4HDaarOAJitVKuOCB1IU8QSvR4Zj8EnAlgE\nwFXgcMzFrwDeYiP1pmGJXk9UCmUPAF8CaCd0LGZIB2A1gIVKteqe0ME0BSzR65hKoXQFsByG02RM\n/coG8BGAz5VqlU7oYBozluh1xLibPhnAEhgqwpiGcxbABKValSh0II0VS/Q6oFIoPQD8BMN16Iww\nimHo3Zew3v1xLNGfkEqhHAjgR7DR9MbiFIAYpVp1UehAGhOW6LVkLDhZCsNpMyJwOMyj8gDMUKpV\n64QOpLFgiV4LKoUyEMAWAB2FjoWp0hYALyvVqgKhAxEamzPORCqFchwMgz8syRu/0QCOGMdQLBpL\ndBOoFMolADYCkAsdC1NjnQCcUimUFv3FzHbda8BYhLIWwHihY2FqTQNgvFKt2il0IEJgPXo1VAql\nHYC9YEne1NkA2K5SKOcLHYgQWI9eBZVC2QKGJO8gdCxMnfoJwEuWdL6dJXolVAqlAsA+GKZKZszP\ndgBjLSXZWaJXQKVQtgNwEICT0LEw9WoLgHFKtUovdCD1jSV6OcZz5MfAykotxSYYBul4oQOpT2ww\nrgyVQukFYD9YkluSGAA/GIuSzJZZvzhTqBRKZwB/gR2TW6LxANYap9o2SyzRUXoK7Q8ASqFjYQTz\nEoCVQgdRXyw+0VUKpTWA32C4goqxbFNUCuXrQgdRHyx6MM64q7YDQLTQsTCNhg5AP6VadVDoQOqS\npffo88GSnHmUGMA2lUJpVqvlWGyPrlIoBwD4HezLjqlYIoAu5rJElEV+yFUKZSsAP8NCXz9TI20A\nrDeXkXiL+6AbZ4bZBjaBI1O9aADvCR1EXbC4RAfwKYAwoYNgmoz3VApld6GDeFIWdYyuUiifA7Bb\n6DiYJicdQFulWpUrdCC1ZTE9ukqhdAKwRug4mCbJF4ZVd5osi0l0AP8F4CJ0EEyT9ZJKoRwsdBC1\nZRG77sZjrENg0zIzT+YGgGClWpUjdCCmMvse3TjKvhosyZkn5wnDXP5NjtknOoC3ASiEDoIxG5NV\nCmWU0EGYyqx33VUKZRCABABWQsfCmJU4AJ2ValWTSR5z79FXgSU5U/fCAIwUOghTmG2Pbhwh/U3o\nOBizdRGGgTmt0IHUhDn36O8LHQBj1gIATBI6iJoyyx6d9eZMA7kDwF+pVuULHUh1zLVHXyh0AIxF\ncAMwS+ggasLsenSVQjkEwK9Cx8FYjFwArZRq1T2hA6mKOfboC4UOgLEodgDeFDqI6phVj86q0xiB\n3AHgrVSrioUOpDLm1qO/LXQAjEVyAzBc6CCqYjaJrlIoQwE8JXQcjMWaLnQAVTGbRAcwUegAGIsW\nqVIoOwodRGXMItGNFWrjhI6DsXiNtlc3i0QH8DyA5kIHwVi80caZjBodc0l0ttvONAYyGNZwa3Sa\nfKKrFEofAE8LHQfDGDXKqrYmn+gwfIOy2WOYxqKzSqH0FDqI8mqc6IQQpwpukvoMroZGCx0Aw5RB\nADwndBDlmdKjnwWQCSAVQJrx31cIIWcJIYIsiKBSKAMBtBaibYapwvNCB1CeKYm+D8CzlFJnSmlz\nAANgWNpoGoRbQH6QQO0yTFV6qhTKRrXklymJ3olS+mfJL5TSvwB0p5SehHDTNTXZebYZsyYBMFDo\nIMoyJdEfEELmEUJ8jLe3AGQRQkQA+HqKr1IqhdIWQLeGbpdhaqhR7b6bkuhjAXjBUB32KwBv430i\nCHNKoQcM35wM0xj1VymUjebzKa7pAyml9wC8Xsl/X6ybcEzCzp0zjZkcQAcAp4QOBDAh0QkhQQDm\nwLDgXOnzKKW96z6sGukjULsMU1Nd0dQSHcB2GOZJXwtAXz/h1IxKoZQDaCNkDAxTA10AfC50EIBp\nia6jlH5bb5GYpg3Y1XBMI0UBqhMh/YobrJVCB2NU46mkCCELAdwF8AuAopL7KaUP6iWyKqgUyikA\nGsuXDmPheIK79+2QrmpJCs4EErsEP+KvsSYOxv/2SHwx8ZagAcK0Hv1F48+5Ze6jAFrVXTg11laA\nNhkGFMjLleHSpRbk4dkAYnXWn3hnOhIPAK6VPKUjgL0NGGKFTBl196vPQEzUTugAGPNHAV2RBJeu\nOePu+VaEnAnk3K64w58SYsrnLwxNIdEJIb0ppQcJIdEV/T+ldFfdh1U5lUJJwAbimHqg43D9riMy\nknyI9kwgcUrxJv7FEtIaT1ZPEVJX8T2JmvToPQAcRMWXm1IADZroAPxgmEubYWqNAlkPbXH5ghfJ\nPRNIbM+3In7ZtsQLhovC6lJgHW+vVqpNdErp+4QQDsAflNJtDRBTddhuO2MSChRqrHAx3Q0Pzvpz\norMBpOUNZ+INw251fWsaiQ4AlFKeEDIdhmo1ofkLHQDTeFGAakW4fLM5bif4Ev2ZIM4lzQMBehEJ\nFSgkeZv1bVwTX0y8K1D7AEwbdd9PCJkDYCuA0tUjBTi95tbA7TGNmJ7g9n17XFW1JIVnAol9gi/x\nL7Am/mhcHYIPDKemBWNKor9s/PlamfuEOL1W2WkMxsxRIDdXhosXPUhOXACxjvcnPvcciDsAd6Fj\nq4Y3gNNCBtAUT6+xRLcAxlNbF6+5IDO+FUfiAkmLdDf4UUI6CB1bLbQUOgBTilokAKYC6G686xCA\n1ZRSbT3EVRWW6GZIxyHjjiOuJ/kS7elA4qRuSQKKJUQBQCF0bHXAWegATNl1/xaG+u+SaaNeMN7X\n0HOqs0Rv4njg/kM5rlzwIvlnAgyntnJsiTcMu7jmSPBppUxJ9M6U0rKntg4SQs7XdUA14CJAm0wt\nGU9tpV1xJw/O+hPJ2QDidbM58YZlrazTTOgATEl0PSHEn1J6CQAIIa3QwOWqKoXSAcLNT8dUgwK8\nVoQrN5xxK8GP0DMBnEuaJwJ4jjTNKxkp1QPIEwH5Yko1EqDQiqfFMsoX2/JUZ8fzejuepw48Txz1\nPHHkeZGjXi9x5HmJg563duR5azuet5HzvLXQL8WURJ8L4B9CyGUYSkR90PDLz9g3cHtMFfQEt+/Z\n42qKNyk4E0Qck3yIf4GVwKe2KNUSII8zJGeBlKLQitJiG2Nyynme2vM8b0hOvTE5eWkznpfY6/VW\nDjxvY8/zNnKeymWUygA4GG9PQvD12EwZdf+bEFIyjzoBoKaUFlXztLpWs5paps5RICfHBpcuepCc\nMwHE+pw/8blvX0entigtJkCuCNAYkpMak5NqbXleZ8dT3p7nqYMhMblmel7kwPMSR73eypHnrex5\nXmav523klJdbUVjDsKss+O5yGYLvhZoy6m4Nwxzu3WBIuKOEkFWU0sL6Cq4CDT7brCWigLZQgosZ\nrsg814oTnQkk7ldd0QplT21RWkgovScC8iWUFkopLbSmtFjGU62c8no7nuft9TwceR4Oel7UzLhb\n68DzVo56Q3La8bytnOflUkAKwzF7c0rBA9AZb1oKoqeAzvCT6CiInjf8W8+D0/MQ6XhIsvXgHuSA\n4/UlNyrideCoDiJeBxH0EFGt8d9aKqZaiKCFmGohJlqIaTEVEy3EKMYjP0kxlZBiiInxd5Hxd04L\nMWf8KSqmhp9aw+/iYog5HURiLRWLtBCJdRAVnxPmT1nKlF33DQByAXxl/H0MgI0ARtR1UFUQdAor\nc6fjkH7fgVy72oLk8yKil/CgIfcI3zET9614JFtRQqwoiDUFRygHPTjowVE9OOgpBz1E0IODIbE4\nYkwwogMHPUQkGyLcB0cM94mIjnBEDzHRgeN0EEFPRBwP0hAzB5FKfpZFjTdw0MEaOlgDtTqVXCSS\nXBd65TBTEr11uVH3fwQYdWc9ej0S8/B1y6K+blnsCKmOCT5BpCnzuscTQp4q+YUQEgHgeN2HVCWW\n6ExTpBM6gJpMPJEIwy6MBMB4QkiG8XcfACn1G95j2K470xQ1/kRHDRcyJIQ0o5RmPWE81WE9OtMU\nNfRl4o+pycQTV2u4rb9hmAivPgn+hjFMLdwXOgBTjtGrU++jpUq1qgCApr7bYZg6dlvoAOoy0Rtq\nqPZOA7XDMHVF8Hnd6zLRGwpLdKapMasevaGWSGKJzjQ1TadHJ4R8Rgipao7qhlrd9FoDtcMwdaVJ\n9ehqAN8RQmIJIVMIIY9U9DTgJJE1PQvAMI1F0+nRKaVrKaVdAYyHYY30BELIz4SQXvUVXCVYojNN\niQ7APaGDMOkYnRAigmEOLwUMwZ8HMIsQsqUeYqtMegO2xTBP6qJSrRL8Qi9TylSXAxgCw4UxSyil\nJRfqLyWEXKiP4CqRAsOlsKIGbJNhaite6AAA03r0JABtKaWTyyR5ifA6jKlKSrUqH4bxAoZpCppc\nov8IIJoQ8h4AEEK8CSHhAEApza6H2KpypoHbY5jaanKJ/g2ASBgmnAAMk1B8U+cR1QxLdKapOCt0\nAIBpE09EUEo7EkLiAYBSmkUIkdZTXNWJE6hdhjFFhlKtaui1CStkSo+uNY66UwAghLhAuLLRc2gE\nNb4MU41GsdsOmJboKwD8AsCVEPIRgGMAPq6XqKphrGJr6EkvGMZUjSbRTZnueRMhJA6GS10JgOcp\npap6i6x6pwG0FbB9hqnOUaEDKGHKte6vUErVlNJvKKVfU0pVhJBP6jO4ahwQsG2GqU4OmmKiAxhO\nCIkp+YUQshLCroO2D2zGGabx2q9UqxrN59OURI8GMIEQMoYQsgFAMaX0lXqKq1pKteohGtE3JsOU\ns1foAMqqNtEJIU6EECcAMhiWSJ4Hw27Jh8b7hbRH4PYZpiIUwP+EDqIsQmnVM0ARQq7AEDgp9xMA\nQCltVZ8BVkWlULYCcEmo9hmmEmeUalVnoYMoq9oenVLqZ0zmYBiuhDtnvH0FoKqJKOqdUq26DCBZ\nyBgYpgKNarcdMO0YfT0AJQzn078y/nt9fQRlIrb7zjQ2jS7Rm9raaxX5FcDbQgfBMEaX0QhrMZra\n2muPUapVJ8GukmMajx+ValWjW6XSlESPAHCCEJJOCEkH8C+AHoSQREJIQr1EV3OrBG6fYQBD7ceP\nQgdRkWpH3UsfSIhPVf9vwtJNdU6lUDoAuAnARqgYGAaGi2T6Ch1ERUy51r3RTsqoVKuyVQrlZgCC\nXcDDMGjEe5ZNcaWWynwrdACMRbsOw8Bwo2Q2ia5Uq+JgqGhjGCGsVqpVeqGDqIzZJLoR69UZIRQB\nWCN0EFUxt0TfAiBT6CAYi7NaqVY16jUBzSrRjTPP/FfoOBiLogGwROggqmNWiW60EmzFVabhfN3Y\ne3PADBNdqVZpACwVOg7GIuSiiexB1viCmboQFxfnKhaL1wIIRX1+yVBKSFaWJ3ieLdvE1B+ZLJva\n2j408Vk8gCSdTjcxLCzsbn2EVRFTilqevDGxeK27u7vSxcUli+O4ev2G0d2/z2tv3fKtzzYYy0U4\nTm8VFHSFiMUmnVLjeZ5kZmYG3759ey0Maxk2iIbedQ91cXHJqe8kBwCRk9N9IrUqqO92GMskcnK6\nbWqSAwDHcdTFxSUbhr3aBtPQic41RJIDACEEEne36w3RFmNZiERSJHZ1rfUAnDEHGjT3zG4wriyR\nvX2OyM6uUSyJw5gPSYsWV0kDdVh1pUGP0cvzfXtvWF1uL/2TgY+tySbx8MzgL6bZU72+Tl6rS3g4\nMk+dws27dzHnk0/w8/LlVT7++alT8eNSw0mArf/7HyaPHl3nbdSVqzduYNj06Tjzyy91ul1Fv344\ntmULnJs1M/m5G3fvxtnkZHw+f75Jz7t64wZOnjuHUQMHmtxmZfq99BKWLFiQ3S00NLfONtpAzLpH\nBwAiEevF7u4Zdb1dD1fXGiXg7m+/haO9PbJzc7Fm69Z6aaO29PpGe2n2E7t68ya2/s+0iVirez8o\nQHl7+9tPEpdQzD7RAUDcrFkWJ5c/BICPV61C+8GDMWjSJLz41lv44scfAQCXr13DkClT0GXkSDz9\n4ou4cPkyACD9+nX0jIlBt9Gj8cFXX5Vu8+qNG+g0dCgAQFNQgHGzZyM8OhovzJmD7mPHIi7ZMGel\nol8/3MvKwrtffIHL164hYvhwvLNsGfI0Gjw7cSIiR45E56FDsefgwcfiLttGysWLiBozBhHDhyM8\nOhoXrxqqhjfv2VN6//QPPij9sB44cQI9Y2IQOXIkYmbNQp5GUxrPkm+/RZ/x47Hrr79wNjkZEcOG\noWdMDFZv2VLh+0cpxTvLlqHT0KHoPHQoduzbBwA4cvo0nnnxRYx68010fO45vP7hh+D5ytfdLCgs\nxJApU/D9jh0AgJFvvIEuI0ci7PnnsW779tLHbfjlF7QdNAh9J0zAv+fOld6/99AhdB87Fk+NGIGB\nEyfizr17AICjp08jYvhwRAwfjqdGjEBufj7e/eILnDh7FhHDh+OrDRug1+vxzrJl6DZ6NMKjo7F2\n27bS19D/5Zcx4a230Dk6Gldv3ED7wYMxaf58hEdHY+ysWdAUGMZ0dUARRCIeAGJiYrxDQ0OVAQEB\nITNnzvQoidHT07PNzJkzPYKDg5VBQUHB8fHx1pW+IQ1I0F33hiT18Lh6Yvduu90HDoj+3b4dOr0e\nXUaORIfgYADA9A8+wIp330WAjw9OJSRgxkcf4Y916zBn6VJMGjUKMUOGYNXmzRVu+7utW+Fob49T\nu3YhOS0NT40Y8dhjFs2YgZSLFxFr/JDrdDps+eIL2MvluJeVhZ4xMRjUqxcIIRW2sXbbNrwWE4PR\ngwahWKuFXq+H+vJl7PjzTxzcsAESiQRvLl6MLXv3ol9UFJauXo29a9bA1sYGy9atw4r16/HO1KkA\nAGsrK/y9YQMAIDw6Gsv+8x9Ede6Md5Ytq7DtXw8cQIJajdgdO3AvKwtRY8aga5jhqOtMUhLO7t4N\nbw8PPDdlCn49cABD+z4+90K+RoMX587F2CFDEDPEcFZp1aJFcHJwQEFhIaLGjMHzzzyDYq0Wi1eu\nxPGtW+FgZ4f+L7+MdgoFAKBLhw44vGkTCCH4YedOfP7DD/hk7lx8sX49vpg/H5EdOiBPo4G1VIpF\nM2bgix9/xK5vvgEArNu+HfZyOY5t2YKi4mL0fuEFPN2lS+lrOLNrF3y9vHD1xg2kpqfj2w8/RGSH\nDpj87rv4butWzHrttWy9YTVhAMDy5ctvuLm56XU6Hbp06dI6NjZWFhERUQAAzs7OupSUFNUnn3zi\n8sknn7ht3bpV8LkcLCbRiVSqO5SYmD2oVy8nmbXhS/bZHj0AAHkaDU6eO4eY2bNLH19cXAwAOBkf\nj83G3eexgwfj3c8/f2zbJ86exWvjxgEAQgIDERoUVG08lFK8/+WXOB4XB8JxuHn3Lu7cvw93Z+cK\nHx/Rrh3+u2YNbty5g+eefhoBPj745+RJxKekoNuYMQCAwqIiuDg54VRCAtSXL6P3+PEAAK1Wi/B2\n/z+v5/D+/QEA2bm5eJibi6jOhinIxwwahL+OHavw9Y149lmIRCK4OTsjqlMnxCUlwV4uR6fQUPi1\nbAkAGPHsszgRH19hoo984w3MfOkljB40qPS+lZs2Yc/ffwMArt++jUtXr+L2vXvo3rkzXJycSmNN\nS08HANy4cwfj587F7cxMFOt08PH0BABEtm+PeZ9+itEDB2JInz7wcnd/rP2///0XSamp+GX/fgBA\nTl4eLmZkQCqRoFNoKHy9vEof6+XujsgOHUrfk5U//4x5Hh5XAZSuYbB+/XqnH3/80Vmn05HMzEzJ\n+fPnrUsSfezYsVkAEB4ervntt99MH5ioBxaT6ACgE4vzeY6zBWBV9n6e5+FgZ1fa25ZXWS9bojZX\nF27Zuxf3srJwfOtWSCQSKPr1Q1FRUaWPHzVwIDq3bYt9R45gyOTJWPnBBwClGDdkCD6cMeORx+49\ndAi9IyOx/r8VX51pI5OVxl3dawPKrNZRgfLPr2xrT3XogL+OHcOogQNBCMGR06fxz8mT+Oenn2Aj\nk6HfSy+h0PjlWllMsz/+GK+PH49BvXrhyOnT+GjlSgDAnIkT0b97d/x59Ch6xsRg75rHK0YppVj2\nn//gma5dH7n/yOnTpe9HZa9JS0g+Z2VVuo6aWq2Wfv31125xcXEqFxcX/bBhw3wLCwtLD4Otra0p\nAIjFYqrT6ap/gxuARRyjl+jZs2feb//8wxfxfFGeRoN9Rw1Lt9nL5fD19MSuP/8EYPhQJFy4AMDw\nAd3+xx8ADMlZkS4dO2Kn8bmqS5eQnJb22GPktrbIzc8v/T0nLw8uTk6QSCQ4fOoUMm7erDL2K9eu\nwc/LC9NiYjCwVy8kpaai51NP4Zf9+3H3/n0AwIPsbGTcvInwtm3xb3w8LmUYxiA1BQWlvWJZjvb2\ncJDLceLsWQDA1kpeX9ewMOzctw96vR6ZDx7gWFwcOrVpA8Cw25t+/Tp4nsfOffvQpWPHCrfx7muv\nwcnREW8uXgzAsDfhaG8PG5kMFy5fxqkEw/yindu2xZHTp3H/4UNotVrs+uuv0m1k5+XBw9UVAPDT\nr/8/mcvla9cQGhSE2a+8go4hIbhw5QrkNjbIK/N+P92lC9Zs3Qqt1pCvaenpyDeOW5R37dYtxBrH\nBn7et68gomvXrLL/n5WVJZLJZLyTk5P+2rVr4kOHDjlUuKFGRNAevaLTYfWpR48emv79+z/sMGRI\n81YeHugYHAx7uRwA8MMnn+CNxYux9LvvoNXpMLx/f7Rt3RqfzZuHCfPm4ZtNm/D8009XuN1XR43C\npAULEB4djXZKJUIDA+Fg3G6J5o6OiGzfHp2GDkXfbt0w6+WXMXz6dHQdNQptFQq09vOrMvYdf/6J\nLb//DrFYDDdnZ/xnyhQ4OTjg/ddfx+DJk0F5HmKxGF/Mn4/wdu3w3eLFePGtt0oPQd57/XUE+vo+\ntt1VixZh6nvvQWZtjafL9XYlnuvTB6fOn0fE8OEgAD6aNQvuzs5IvXIFEe3a4d0vvkByWhq6hoVh\nSJ8+lb6GT+fNw+R338X85cvx3vTpWLttG8KjoxHo64vwtoal7lu4uGD+1KnoNW4c3J2d0V6pLB1g\nnD91KsbNng0PNzeEt22LqzduAAC+3rgRR06fhojjoPD3R7+oKHCEQCwWI2LYMIx77jm8Nm4crt68\niS4jR4ICcG7WDFu//LLCOBWtWuGn337Dax9+yHv5+RWtnDPnkTkOIiMjC0JDQzWBgYEh3t7eRWFh\nYXlV/vEagQYtajl//nx6u3bt7jVYgxXIzs7mHBwc+IfXrjn17t/f7+v33y8dkKstvV4PrU4Haysr\nXL52Dc9OnIiE33+HVCKpo6gbpyOnTz8y4GUOSq4liPttj9YqwD+FSCS6+mjn/Pnzzu3atfOtj21X\nxKKO0QFg3LhxPmlpabKioiLyQvSwvA7BwfLqn1U1TWEh+r/8MnQ6HSil+HLBArNPcnMnael1qb6S\nXAgW16OXRSlF8ZUrgbxGYy90LEzjIXFzyxC7uNTrlGQN3aNb1GBceYQQSL29LxOxpFjoWJjGQWRv\nf6++k1wIFp3oAEDEYr3UxzuViERms5vG1A5na/tQ0rKl4Be31AeLT3QA4GSyIqmPzwXCsWS3VJxM\nlif18blck+sKmiKW6EacjU2h1Mc7lXCc+VZ6MBXirKw0Ul/ftKZWemoKYUfdFzrUaZkqFmY/0Xl5\nzta2QOLtnabNyAiiPF/hl6AllqnWJg6dTocPv/kGv/z1V+mVZ9F9+2Leq6/Wacyvzp+PAT16VHjZ\nbUUWr1yJH3buLC2ZpQA9eOjQJRdjsUp17t27J1q7dq3T22+/nQkAv//+u92yZcvc/vnnn4u1fQ0N\ngfXo5Yjk8nxJy5ZpIKTKP7wllanWJo4PvvoKt+/exelduxC7YwcOrF8Pra5xHBm9/sILiN2xA6d+\n+60gITExwcXdvUaDsTqdDvfv3xetW7fOtb5jrGsWl+hz585t4efnF9KlS5fAwYMH+7333ntuAJCc\nnGwVFRUVGBISogzv2dNTnZ2dAUIoK1N9NI6Nu3dj9IwZGDJlCtoMHIj5FXwBaAoK8MPOnVj2zjuw\ntjKUFdjZ2mLBtGmlj1mxfj06DR2KTkOH4uuNG6u9v7Ly4rLOJiej74QJ6DJyJIZMnoxbmZUPnnNW\nVhorP7/UknPlFy5ckIaFhbUODg5WBgcHK/fv328LGHrsiIiIoMGDB/u1bt06ZPbs2V7Xrl2zUigU\nwZMnT/YCgPz8fFH//v1b+fknxt3rAAAWyklEQVT5hQwZMsSvqlJdoVjUBTNHjhyx2bNnT7PExMQU\nrVZL2rdvH9yhQwcNAEycONHnu+++u9qmTZuigwcP2k6dNcvz+P79qXOmTw+aNGoUseQy1fIS1Gr8\nu307rKRStBs8GFPHjn2kYuxSRga8WrSAna1thc8/m5yMjbt34/CmTaAAeowdi26dOoHn+Qrv1/M8\ndh84gIrKi0totVrM/vhjbFuxAi5OTtixbx8WrliB1YsWPdb+ig0b6frffycAghwcHHSxsbGpHh4e\nuqNHj6ba2NjQxMREqzFjxrRKSkpSAUBCQoJtfHx8skKhKL5w4YJ00KBBMrVanQIYvghUKpXs3Llz\nl319fbVhYWGK/fv3y/v169eoLou1qEQ/dOiQfMCAAQ/lcjkFQJ955pmHgOGy2Pj4ePmIESP8Sx5b\nXFxMRHZ2ecfj4/ktK1boAUgttUy1vJ5PPQUHOzsAhuvCM27erLA0tMSGX37BN5s24cHDh/jnp5/w\nb3w8BvfpA1sbGwDAkD59cOLsWVBKK7yf53kM6tUL5cuLy0pNT0fKxYsYZBwD4PV6uLu4PPY4Hcdp\nJk5+9cGiRYsemdyxuLiYvPLKKz4pKSkyjuNw9erV0grHtm3b5isUikp379u0aZPv7++vBYCQkBDN\npUuXpJW+GQKxqESv7CpAvV4POzs7Xcm3dHlWrVqp6c2bAcjLszFlu1VpSmWq5VmVubxXJBJBV+5Y\n39/bG9dv3UJufj7sbG0xfuhQjDfujuv1+krfL1PvL/8Ypb8/Dm3aVOljxC4uN7RSKWdVwWv+6KOP\n3FxdXbU7d+68wvM8ZDJZ6UCxjY1NlfviVlZWpQGKRCI0ltLUsizqGL1nz555f/75p4NGoyHZ2dnc\ngQMHHAHAycmJ9/LyKv7++++bAYb69H///VcGAB07dsxbu369nbRVqws/79tXYV2juZepmspGJsOL\n0dGYtWQJCo1fXnq9HsXGEtGuYWH4/eBBaAoKkK/RYM/Bg+jSsWOl90d27Ij/HT6MwqIilC0vLivI\nzw/3srJKy0u1Wi1SLhoHwgmhEg+PyxI3t0rne8vOzha1aNFCKxKJsHLlyuaVDVQ6ODjo8/Pzm1ze\nCHx67clOh5nKWKaaHRwcHOLp6VnUtm3bfAcHBz0AbN68+fKkSZN8li5d2kKn05GhQ4c+iIyMLFi5\ncmXG6NGjW61cudJt8ODBWRSQodz8CuZeplobC19/HR9+/TU6DR0Kua0tZFZWiBkyBC1cXeHj6Ylx\nzz2H7mPHAgAmREejvVIJAJXeP7BnT0QMHw7vFi0eKS8uIZVIsGn5csz5+GPk5OVBp9fjtXHjEBLU\nWidp6XVJZGdXesy8atUqt23btjUv+f3XX3+9OGPGjLvDhg3z3717d7Nu3brlymSyCntxd3d3fVhY\nWF5gYGBI7969swcPHpxdZ29aPbK4opaSMtXc3FwuMjKy9apVq65269at4hkIKqHLynLU3brlS41r\nu1lqmWpDytNoILexgaagAM9MmICalBdzMlmupGXLK5xUqq3ygQJgZar1rGyZ6ujRo++bmuQAIG7W\n7CFna5usvX7dl9do7FmZav2bvnAhVJcvo6ioCDHPPVdNkhMqdna+KXZzvW2ul7SayuJ69LpEKYUu\nM9NVl5npBUrZJ6oRIBJJkcTL67LI1tbkL/CGxHr0JoQQAomr612RXJ5TfP16K1pcLKv+WUx9Ednb\n35d4emaQGl7Oakma3OhhY8TZ2BRaBQSoxM2a1XrhPab2iEikk3h4XpZ6e6ezJK8Y69HrCOE4KvH0\nvM45OGTpbt9uyRcWVnxZGFOHCBU1c7wrcXO7VZsljC0JS/Q6JpLL8zl/f7U+K8tJd/euJ9XpGt1V\nUuaAs7HJkbRocY2TyQqFjqUpEDTR26xvU6dlqokvJlZ5Xr58iWF9IYRA7OT0QOTomKW7c9dd9+C+\nOyit9jDpxbfeguriRbzw/PPIyslBt7Aw9I6MrHG7NV0Ndc22bbCxti5dGqkxeJiTU6MyXiKRFEnc\n3K6JHB2bxPnrxsKievSSEsP6TvQShOOopIX7LVFzp3u627c9ix48aC4WV/yW3753DyfPncOFMgsW\n1JdJI0fWexumKinjrSzRCcfpRc2b3xa7uNwx5wki6otFDcaVLzHkeR6TJ0/2CgwMDAkKCgpes2ZN\nM8BQkdSpU6fWzzzzjL+/v3/I2LFjvUsuiVy9erVTUFBQcGBgYMjUqVM9S7b9+eefO/v6+oaGh4e3\nHj16tM/48eO9AWDYsGG+r06b5hY1YoR0ztdf3z+dmprTa9w4PDViBHqNG4fUK1cAAENefRWZDx4g\nYvhwHI+Lw6vz5+MXY9K/+/nn6PjccwiPjsZ/PvsMAHDn3j2MevNNRAwbhohhw3DSeOmnXq/HtIUL\nEfb88xj86qsoKHx8z3bxypWlZZ79XnoJC5YvR9SYMWg7aBCOxxl2iqoqva2qBPa9L79Ez5gYdB01\nCvEpKRgyeTJCBgzAGuPqpQDw+Q8/lK5qusg4J3z5Mt4yj6Mdo6O1//nuu0yJm9vt1LQ0SatWrUJG\njx7tExAQENK1a9fAvLw8dmqzGhaV6MuWLbvesmXLIrVanbJ69errGzZscExMTJSpVKrkv//+O/W9\n997zunr1qgQAEhMTbb/88strFy5cSE5PT7fasGFDs/T0dMnChQs9Dx06lJqSkpIcHx9vu3HjRsf0\n9HTJZ5991iI2NlZ19OjR1LS0tEeWyr106ZL18ePHU1evXZse2rPnpcPHjyec+fvv2+9On86/v2IF\nAGD7V1+hVcuWiN2xo3SlUsBw/fpvBw8ibvdunNq1q3SGljmffIKoTp0Qu3MnTmzbBqW/ofDuYkYG\nJo8ejbjdu+FgZ4fdxkUFq6LT63F082b89623sOTbbwE8Wnr79uTJiE8x1Pvcy8oqLYH9d9s2dAwJ\nwYr160u35eXujkObNqFrx46YvGABNi1fjkObNmGxMaEPnDiBi1ev4ujmzTi5YwfiU1Jw7MwZLJox\no/T1L5k9GwdOntSqrl3LPxUffy5JpUo4d/687I8//pADQEZGhvUbb7xx9+LFi8kODg76DRs2NIqF\nDBszi9p1L+/o0aN2I0eOfCAWi9GyZUtdRERE3rFjx2wcHBz4Nm3a5AcHBxcDwMiRIx8cPXpULpFI\n6FNPPZXr4eGhA4BRo0Y9OHz4sBwAIiIict3c3PQAMHTo0KzU1NTSZI+Ojs4q2WV/8OCBaNTUqd7p\n6enWBCjmdToxEYt5ABUO2tnb2sJaKsXU999H/+7dS0s0D586hbVLlgAwVEw52NnhYU4OfD09S5cZ\n7hAcjKvVFMsAwHPGpabKPr6y0tvqSmAH9uxpeE5QEPIKCmBnaws7W1tYSaV4mJODv0+cwN///lta\ns5+v0eBiRgZatmgBAOCsrfNFzZvf2XPqlO3B48ebhbRpowAAjUbDqdVq61atWhV7enoWdenSpQAA\nOnTooElPT39k0UzmcRad6FVdFfjYKqGE1LqMUi6Xl57bnTdvnmePHj1y9+/ff+nChQvS3r17t7Zq\n3ToRefkuPOCFcntZYrEYRzZvxj8nT2LHvn1YvXkz/li3rtK2rKT//30hEolQUEXpawmp8TllS06r\neq1VlcCWtM8R8kg5K8dx0BlLVOe88gomlhknICKRLv3Bg4dawM4qIEBtbMd2xowZt+bOnfvIlZQX\nLlyQSqXSsmWhtKCgwKL2TGvDot6g8iWGPXr0yN2xY4eTTqfDzZs3xadOnZJHRUXlA4Zdd7VaLdXr\n9dixY4dTVFRUbvfu3fNjY2Ptbt26JdbpdNi+fbtTz54986KiovJjY2PtMjMzRVqtFr/++mulu5I5\nOTkiLy+vYgBYvXq1M2D4EiF28mwdUGwVFJQgdnG9zhOiAwzFHNm5uejfvTv+O28eEtRqAEDPiIjS\nOej0ej1y8up2QpPKSm9rWgJbmae7dsWG3buRX1CgF9nb378FXLlnZ5fs6O9/PS8/v/TbdcCAATkb\nN250zs7O5gDgypUrkhs3blh0x/QkBH3jqjsdVtfKlxh+++2310+cOCFXKpUhhBD6wQcfXPf29tYl\nJCSgffv2ebNnz/ZSq9WyiIiI3BdeeOGhSCTCe++9d6NHjx5BlFLSp0+f7HHjxj0EgJkzZ97q3Lmz\n0tXVVRsUFFRQUv5a3rx5825PnDjRb8WKFe5RUVE55f+fk0q1nJvrnUKRSFZkb5+fJ5GIh7/yintx\nURFHKcXSt94CYFiZdPoHH2D9rl3gRCKsWLCgwhlVaquy0lsXJ6cal8A+hhDar2/fhwnXr+u7jB0r\nByE2NjY21ps2bcoPCQkpKvu3Wb169fXk5GTrzp07KwDD5A+bNm26IhaL2Yh7LbCilgrUZgrfkvJX\nrVaLfv36BUyYMOHe+PHjH9ZVTHxxsYTPzbXnNRo5r9HIqVZrXf2zaq9OSm8JoZy1dR5nY5PLyeV5\nnK1tHjs1ZsCKWpqouXPnehw5csS+qKiI9OjRI6ekp68rnFSq5Zo3v4/mze8DANVqxfq8PEPSazR2\nfFGxDKi7Crpald4SwnPWsnzO1iaXs7XN5Wxt81liNw6sRzcTVK/naGGhNV9UbEWLi6xosdaKaout\nqVZrRXW6OiyOJ5RIxMVEKi0kUmkRJ5UWEisrw00q1bL675phPTpTK0Qk4omtrYaroA6b8jyhRUVW\nVKuVgOc5ylMOPM+B8hzleZHhPp4DpYRwHA9CKDiOJxynh0ikJyKRHiKRjojFOiKVFrNeuulhiW4B\nCMdRIpMVghWAWCyLOr3GMJaKJTrDWABBd91VCmWdlqkq1ap6Py9vY2PTQaPRxKenp0umTJnSct++\nfZerenyPHj0Cdu7ceQUAaloia2obdcW43FBgWlpacl1u98MPP3SdOXPmPTs7uzqd/SU8PLz1Z599\ndq179+51Oj/cxo0bHYODgwvDwsLM5lCH9ei15Ovrq61JAh4+fPiis7OzvjarcNa0jdrSNdDqpqtX\nr3bLy8trEp81rVaL3bt3OyYkJJjV/H9N4s2vSzVZTTUsLKx1fHy8NQCo1Wpp+/btFaGhoco333zT\no2Q7Fy5ckAYGBoYAQG5uLvfss8+2CgoKCh44cGCrtm3bKo4cOWIDAJ6enm1u3bolLl8im52dzUVG\nRgYFBwcrg4KCgn/66SfH8rGWbePMmTPWbdq0USoUiuCgoKDgxMREKwBYuXKlU8n9Y8eO9SlJ3l27\ndtm3b99eERwcrBwwYECrkktJPT0928yZM6dFWFhY6++//77Z0aNHbVq3bh3cvn17xfLlyyv8IqpN\nOW+JxYsXu969e1fSo0ePoIiIiCCg8lJfGxubDpMmTfIKDg5WRkZGBt28eVMMACdOnJC1a9dOERQU\nFPzMM8/4Z2Zmisq2odfrER0d7fvGG294AEBMTIx3aGioMiAgIGTmzJmlf7M5c+a0CA0NVQYGBoaM\nGTPGp2TV0/Dw8NbTp0/37Ny5c+sFCxa4HzhwwHHBggVeCoUiODk52SwKZiwq0cuuprp3795LCQkJ\npfO6TZw40WflypUZycnJqk8//fT61KlTvQFg2rRp3hMnTsxMSkpSubu7V7gQwKeffuri6OioT01N\nTVm4cOHNlJSUx+aLK18ia2Njw+/du/diSkqK6vDhw6nvvPOOV1XL7X711Vcu06ZNu6NWq1MSEhJU\nfn5+xWfPnrXesWOH05kzZ9RqtTqF4zi6atWq5rdu3RIvWbKkxZEjR1JTUlJUHTt21CxatMitZFvW\n1tZ8XFzchVdffTXrlVde8V2+fHnGuXPn1JW1bWo5b9nnLliw4K6rq6v28OHDqbGxsamVlfoCQEFB\nAdexY0dNSkqKqmvXrrlvv/22BwBMmDDBb8mSJddTU1NTQkJCCubNm1eavFqtljz//PN+gYGBhStW\nrLgJAMuXL7+RlJSkUqvVycePH7eLjY2VAcDcuXPvJiUlqdLS0pILCgq4LVu2OJRs5+HDh6LTp09f\nWLp06e2nn3764eLFi6+r1eqUkJCQ6quCmgCLSvSyq6k2a9aMr2g1VYVCETxt2jSfu3fvSgDg7Nmz\n8kmTJj0AgMmTJ9+vaLsnTpyQjxkz5gEAdO7cuTAoKKjaY0ae58mMGTO8goKCgnv16hV09+5d6fXr\n1ysdM4mMjMxftmxZi/nz57unpaVJ5XI53bdvn11SUpJNu3btlAqFIvjYsWP2ly9ftjp06JDtpUuX\nrMPDwxUKhSJ4y5YtzTMyMkrL2saPH58FGGbcyc3NFQ0cODAPAF5++eUKX19l5byAYSXR4ODgYrFY\nXFrOW9XrPnbsmG1Jqa9EInmk1JfjOEycOPFBSSynTp2Sl49x0qRJ90+ePFnaxrRp03yCg4MLli5d\nWrqu2vr1652M65wHp6WlWZ8/f94aAP744w+7tm3bKoKCgoJPnDhhl5SUVLp7XvL3M1cWdR69tqup\nctVcIFKbqwtXr17tdP/+fXFiYqLKysqKenp6tqmq3HLKlCkPoqKi8n/55ReHAQMGBK1cuTKdUkpG\njBhx/5tvvrlR9rE///yzQ7du3XL27NlzpaJtlQyK1XQ1VVPLeWu7req2XZFOnTrlHT161F6j0dyx\nsbGharVa+vXXX7vFxcWpXFxc9MOGDfMtLCzkNBoNmT17tk9sbGxKQECAdtasWR6FhYWl73ddDxQ2\nNhbVo9d2NdU1a9Y4AcCaNWuaV7TdLl265G3ZsqUZAMTFxVmnpqY+NpBTvkQ2Oztb5OzsrLWysqJ7\n9uyxu3nzZpWzxaakpEiVSmXRggUL7vbt2/fhuXPnZP3798/5/fffm5WUb965c0eUmpoq7dmzZ/6Z\nM2fkSUlJVoBhDCEhIeGxY01nZ2e9XC7X//nnn3IA+PHHH50qatvUct7yz7e1tdWXjBFUVupb8r7/\n8MMPzYyxNA8PD89t3ry53t7eXr9v3z45AKxbt655ZGRkaU3u5MmT7/Xt2zd70KBB/lqtFllZWSKZ\nTMY7OTnpr127Jj506JADYJi4AgDc3d112dnZ3J49eyotJZbL5fqcnByzyg1Be/SGOB1W1pOupjpk\nyJCsirY7d+7czJEjR/oGBQUFh4aGalq3bl3QrFmzR0alypfILly48PaAAQMCQkNDlSEhIRo/P78q\nT+Vs3LjRafv27c3FYjF1cXHRfvzxxzfd3Nz0CxYsuNGnT58gnuchkUjoihUrMvr06ZO/evXq9NGj\nR7cqLi4mAPD+++/faNu27WPHm+vWrUufOHGir0wm43v37v1Y2SwAvPDCCw9NKect//wXX3zx3oAB\nAwJdXV21sbGxqZWV+spkMj45OVkWEhLibmdnp9+1a9dlAPjhhx+uTJ061eeNN97gvL29izZv3pxe\ndvsLFy68M3PmTFF0dLTf7t27r4SGhmoCAwNDvL29i8LCwvIAw5daTExMZnBwcIiXl1dxu3bt8svH\nWSImJubB1KlTfVetWuW2Y8eOS+ZwnG5xRS11sZpqeTqdDsXFxcTGxoYmJydb9e3bN+jSpUtJ1tbW\nZn1NeG3KeatScv1AXWyrsWNFLfWsLlZTLS83N5eLiopqrdVqCaUUn3/++VVzT3KmabG4Hp1hGoOG\n7tEbesCB53meFSwzFs2YAw06yt/QiZ6UmZnpwJKdsVQ8z5PMzEwHAEkN2W6DHqPrdLqJt2/fXnv7\n9u1QWNipPYYx4gEk6XS6iQ3ZaIMeozMMIwzWqzKMBWCJzjAWgCU6w1gAlugMYwFYojOMBWCJzjAW\ngCU6w1gAlugMYwFYojOMBWCJzjAWgCU6w1gAlugMYwFYojOMBWCJzjAW4P8AwbGLZRMTu+wAAAAA\nSUVORK5CYII=\n",
+      "text/plain": [
+       "<matplotlib.figure.Figure at 0x13469240>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "df_plot = df.groupby('methode_xy').count().pkey_boring.sort_values()\n",
+    "\n",
+    "ax = df_plot.plot.pie(labels=None)\n",
+    "\n",
+    "ax.set_aspect('equal')\n",
+    "ax.legend(loc=3, labels=df_plot.index)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<matplotlib.legend.Legend at 0x13ab20f0>"
+      ]
+     },
+     "execution_count": 16,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<matplotlib.figure.Figure at 0x3817ba8>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "df_plot = df.groupby('methode_mv').count().pkey_boring.sort_values()\n",
+    "\n",
+    "ax = df_plot.plot.pie(labels=None)\n",
+    "ax.set_aspect('equal')\n",
+    "ax.legend(loc=3, labels=df_plot.index)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<matplotlib.legend.Legend at 0x134eee10>"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<matplotlib.figure.Figure at 0x134de240>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "df_plot = df.groupby('methode_aanvangspeil').count().pkey_boring.sort_values()\n",
+    "\n",
+    "ax = df_plot.plot.pie(labels=None)\n",
+    "\n",
+    "ax.set_aspect('equal')\n",
+    "ax.legend(loc=3, labels=df_plot.index)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Listing GxG for GrondwaterFilters"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For some of our Grondwaterfilters precalculated groundwaterlevel statistics (GxG) are available next to the individual measurements (peilmetingen) themselves. These statistics give information about the average high, low and medium groundwater levels at that location.\n",
+    "\n",
+    "They can be obtained by defining a new subtype for the GrondwaterFilter type:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.types.fields import XmlField, XsdType\n",
+    "from pydov.types.abstract import AbstractDovSubType\n",
+    "from pydov.types.grondwaterfilter import GrondwaterFilter\n",
+    "\n",
+    "\n",
+    "class Gxg(AbstractDovSubType):\n",
+    "    \n",
+    "    rootpath = './/filtermeting/gxg'\n",
+    "\n",
+    "    fields = [\n",
+    "        XmlField(name='gxg_jaar',\n",
+    "                 source_xpath='/jaar',\n",
+    "                 definition='jaar (hydrologisch jaar voor lg3 en hg3, kalenderjaar voor vg3)',\n",
+    "                 datatype='integer'),\n",
+    "        XmlField(name='gxg_hg3',\n",
+    "                 source_xpath='/hg3',\n",
+    "                 definition='gemiddelde van de drie hoogste grondwaterstanden in een hydrologisch '\n",
+    "                            'jaar (1 april t/m 31 maart) bij een meetfrequentie van tweemaal per maand',\n",
+    "                 datatype='float'),\n",
+    "        XmlField(name='gxg_lg3',\n",
+    "                 source_xpath='/lg3',\n",
+    "                 definition='gemiddelde van de drie laagste grondwaterstanden in een hydrologisch jaar '\n",
+    "                            '(1 april t/m 31 maart) bij een meetfrequentie van tweemaal per maand',\n",
+    "                 datatype='float'),\n",
+    "        XmlField(name='gxg_vg3',\n",
+    "                 source_xpath='/vg3',\n",
+    "                 definition='gemiddelde van de grondwaterstanden op 14 maart, 28 maart en 14 april in '\n",
+    "                            'een bepaald kalenderjaar',\n",
+    "                 datatype='float')\n",
+    "    ]\n",
+    "\n",
+    "\n",
+    "class GrondwaterFilterGxg(GrondwaterFilter):\n",
+    "    subtypes = [Gxg]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from pydov.search.grondwaterfilter import GrondwaterFilterSearch\n",
+    "from owslib.fes import PropertyIsEqualTo\n",
+    "\n",
+    "fs = GrondwaterFilterSearch(objecttype=GrondwaterFilterGxg)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'cost': 10,\n",
+       " 'definition': 'gemiddelde van de grondwaterstanden op 14 maart, 28 maart en 14 april in een bepaald kalenderjaar',\n",
+       " 'name': 'gxg_vg3',\n",
+       " 'notnull': False,\n",
+       " 'query': False,\n",
+       " 'type': 'float'}"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fs.get_fields()['gxg_vg3']"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[000/001] c\n"
+     ]
+    }
+   ],
+   "source": [
+    "df = fs.search(\n",
+    "    query=PropertyIsEqualTo('pkey_filter', 'https://www.dov.vlaanderen.be/data/filter/1999-009146')\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>pkey_filter</th>\n",
+       "      <th>pkey_grondwaterlocatie</th>\n",
+       "      <th>gw_id</th>\n",
+       "      <th>filternummer</th>\n",
+       "      <th>filtertype</th>\n",
+       "      <th>x</th>\n",
+       "      <th>y</th>\n",
+       "      <th>mv_mtaw</th>\n",
+       "      <th>gemeente</th>\n",
+       "      <th>meetnet_code</th>\n",
+       "      <th>aquifer_code</th>\n",
+       "      <th>grondwaterlichaam_code</th>\n",
+       "      <th>regime</th>\n",
+       "      <th>diepte_onderkant_filter</th>\n",
+       "      <th>lengte_filter</th>\n",
+       "      <th>gxg_jaar</th>\n",
+       "      <th>gxg_hg3</th>\n",
+       "      <th>gxg_lg3</th>\n",
+       "      <th>gxg_vg3</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/filter/1999...</td>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/put/2018-00...</td>\n",
+       "      <td>HOSP063</td>\n",
+       "      <td>1</td>\n",
+       "      <td>peilfilter</td>\n",
+       "      <td>26878.0</td>\n",
+       "      <td>199250.0</td>\n",
+       "      <td>4.25</td>\n",
+       "      <td>De Panne</td>\n",
+       "      <td>9</td>\n",
+       "      <td>0120</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>1.69</td>\n",
+       "      <td>0.75</td>\n",
+       "      <td>2000</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>3.99</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/filter/1999...</td>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/put/2018-00...</td>\n",
+       "      <td>HOSP063</td>\n",
+       "      <td>1</td>\n",
+       "      <td>peilfilter</td>\n",
+       "      <td>26878.0</td>\n",
+       "      <td>199250.0</td>\n",
+       "      <td>4.25</td>\n",
+       "      <td>De Panne</td>\n",
+       "      <td>9</td>\n",
+       "      <td>0120</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>1.69</td>\n",
+       "      <td>0.75</td>\n",
+       "      <td>2001</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>4.14</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/filter/1999...</td>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/put/2018-00...</td>\n",
+       "      <td>HOSP063</td>\n",
+       "      <td>1</td>\n",
+       "      <td>peilfilter</td>\n",
+       "      <td>26878.0</td>\n",
+       "      <td>199250.0</td>\n",
+       "      <td>4.25</td>\n",
+       "      <td>De Panne</td>\n",
+       "      <td>9</td>\n",
+       "      <td>0120</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>1.69</td>\n",
+       "      <td>0.75</td>\n",
+       "      <td>2002</td>\n",
+       "      <td>4.29</td>\n",
+       "      <td>3.51</td>\n",
+       "      <td>4.17</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/filter/1999...</td>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/put/2018-00...</td>\n",
+       "      <td>HOSP063</td>\n",
+       "      <td>1</td>\n",
+       "      <td>peilfilter</td>\n",
+       "      <td>26878.0</td>\n",
+       "      <td>199250.0</td>\n",
+       "      <td>4.25</td>\n",
+       "      <td>De Panne</td>\n",
+       "      <td>9</td>\n",
+       "      <td>0120</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>1.69</td>\n",
+       "      <td>0.75</td>\n",
+       "      <td>2003</td>\n",
+       "      <td>4.10</td>\n",
+       "      <td>2.82</td>\n",
+       "      <td>4.04</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/filter/1999...</td>\n",
+       "      <td>https://www.dov.vlaanderen.be/data/put/2018-00...</td>\n",
+       "      <td>HOSP063</td>\n",
+       "      <td>1</td>\n",
+       "      <td>peilfilter</td>\n",
+       "      <td>26878.0</td>\n",
+       "      <td>199250.0</td>\n",
+       "      <td>4.25</td>\n",
+       "      <td>De Panne</td>\n",
+       "      <td>9</td>\n",
+       "      <td>0120</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>onbekend</td>\n",
+       "      <td>1.69</td>\n",
+       "      <td>0.75</td>\n",
+       "      <td>2004</td>\n",
+       "      <td>3.84</td>\n",
+       "      <td>2.95</td>\n",
+       "      <td>3.91</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                         pkey_filter  \\\n",
+       "0  https://www.dov.vlaanderen.be/data/filter/1999...   \n",
+       "1  https://www.dov.vlaanderen.be/data/filter/1999...   \n",
+       "2  https://www.dov.vlaanderen.be/data/filter/1999...   \n",
+       "3  https://www.dov.vlaanderen.be/data/filter/1999...   \n",
+       "4  https://www.dov.vlaanderen.be/data/filter/1999...   \n",
+       "\n",
+       "                              pkey_grondwaterlocatie    gw_id filternummer  \\\n",
+       "0  https://www.dov.vlaanderen.be/data/put/2018-00...  HOSP063            1   \n",
+       "1  https://www.dov.vlaanderen.be/data/put/2018-00...  HOSP063            1   \n",
+       "2  https://www.dov.vlaanderen.be/data/put/2018-00...  HOSP063            1   \n",
+       "3  https://www.dov.vlaanderen.be/data/put/2018-00...  HOSP063            1   \n",
+       "4  https://www.dov.vlaanderen.be/data/put/2018-00...  HOSP063            1   \n",
+       "\n",
+       "   filtertype        x         y  mv_mtaw  gemeente  meetnet_code  \\\n",
+       "0  peilfilter  26878.0  199250.0     4.25  De Panne             9   \n",
+       "1  peilfilter  26878.0  199250.0     4.25  De Panne             9   \n",
+       "2  peilfilter  26878.0  199250.0     4.25  De Panne             9   \n",
+       "3  peilfilter  26878.0  199250.0     4.25  De Panne             9   \n",
+       "4  peilfilter  26878.0  199250.0     4.25  De Panne             9   \n",
+       "\n",
+       "  aquifer_code  grondwaterlichaam_code    regime  diepte_onderkant_filter  \\\n",
+       "0         0120                     NaN  onbekend                     1.69   \n",
+       "1         0120                     NaN  onbekend                     1.69   \n",
+       "2         0120                     NaN  onbekend                     1.69   \n",
+       "3         0120                     NaN  onbekend                     1.69   \n",
+       "4         0120                     NaN  onbekend                     1.69   \n",
+       "\n",
+       "   lengte_filter  gxg_jaar  gxg_hg3  gxg_lg3  gxg_vg3  \n",
+       "0           0.75      2000      NaN      NaN     3.99  \n",
+       "1           0.75      2001      NaN      NaN     4.14  \n",
+       "2           0.75      2002     4.29     3.51     4.17  \n",
+       "3           0.75      2003     4.10     2.82     4.04  \n",
+       "4           0.75      2004     3.84     2.95     3.91  "
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df.head()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/objecttypes.svg b/docs/objecttypes.svg
new file mode 100644
index 0000000..6f409e7
--- /dev/null
+++ b/docs/objecttypes.svg
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="204.47354mm"
+   height="84.83876mm"
+   viewBox="0 0 204.47354 84.83876"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="objecttypes.svg">
+  <defs
+     id="defs2">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5907"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5905"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Send"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5837"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5835"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4913"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInM"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4916"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.4,0,0,-0.4,1.8,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleOutL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleOutL"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4922"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,-4.8,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4789"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Send"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Send"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4777"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4765"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL-0"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4913-6"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL-3"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4913-64"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5907-7"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path5905-7"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6)" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL-0-9"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4913-6-2"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL-3-4"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4913-64-8"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="EmptyTriangleInL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="EmptyTriangleInL-0-9-5"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4913-6-2-1"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5907-73"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path5905-5"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5907-7-7"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         inkscape:connector-curvature="0"
+         id="path5905-7-4"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="505.54136"
+     inkscape:cy="45.003721"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1017"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:lockguides="true"
+     fit-margin-top="5"
+     fit-margin-left="5"
+     fit-margin-right="5"
+     fit-margin-bottom="5" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-2.2067067,-8.7319984)">
+    <rect
+       style="fill:#f0f0f0;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.19595593;stroke-miterlimit:4;stroke-dasharray:0.19595592, 0.39191184;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4485"
+       width="57.957817"
+       height="54.182285"
+       x="7.3046846"
+       y="13.829983" />
+    <rect
+       style="opacity:1;vector-effect:none;fill:#f0f0f0;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.1961233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.19612331, 0.39224661;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4485-7"
+       width="128.35388"
+       height="54.182098"
+       x="73.228302"
+       y="13.83006" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="7.3077068"
+       y="13.833004"
+       id="text4519"><tspan
+         sodipodi:role="line"
+         id="tspan4517"
+         x="7.3077068"
+         y="23.488499"
+         style="stroke-width:0.26458332" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="9.4494038"
+       y="19.376488"
+       id="text4523"><tspan
+         sodipodi:role="line"
+         id="tspan4521"
+         x="9.4494038"
+         y="19.376488"
+         style="font-size:3.52777767px;stroke-width:0.26458332">pydov.search</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="75.435165"
+       y="19.565475"
+       id="text4523-3"><tspan
+         sodipodi:role="line"
+         id="tspan4521-4"
+         x="75.435165"
+         y="19.565475"
+         style="font-size:3.52777767px;stroke-width:0.26458332">pydov.types</tspan></text>
+    <g
+       id="g4656"
+       transform="translate(0,1.3839262)">
+      <g
+         id="g4650">
+        <rect
+           style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.259;stroke-miterlimit:4;stroke-dasharray:1.55399999, 0.518;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect4543"
+           width="46.722584"
+           height="14.216628"
+           x="13.207905"
+           y="26.433645" />
+        <g
+           id="g4645">
+          <rect
+             y="48.175911"
+             x="13.207905"
+             height="14.216629"
+             width="46.722584"
+             id="rect4543-3"
+             style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.259;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        </g>
+      </g>
+    </g>
+    <rect
+       style="opacity:1;vector-effect:none;fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27537987;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.6522791, 0.5507597;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-4"
+       width="52.843773"
+       height="14.210012"
+       x="77.755524"
+       y="27.920254" />
+    <rect
+       style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27537987;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-3-5"
+       width="52.843773"
+       height="14.210013"
+       x="77.755524"
+       y="49.463768" />
+    <rect
+       style="opacity:1;vector-effect:none;fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27627483;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.65764895, 0.55254966;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-4-4"
+       width="53.163059"
+       height="14.21663"
+       x="144.04947"
+       y="27.6329" />
+    <rect
+       style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27627483;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-3-2"
+       width="53.163059"
+       height="14.21663"
+       x="144.04947"
+       y="49.744507" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="35.71875"
+       y="36.007439"
+       id="text4668"><tspan
+         sodipodi:role="line"
+         id="tspan4666"
+         x="35.71875"
+         y="39.185101"
+         style="font-size:3.52777767px;stroke-width:0.26458332" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Italic';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="35.529758"
+       y="36.19643"
+       id="text4672"><tspan
+         sodipodi:role="line"
+         id="tspan4670"
+         x="35.529758"
+         y="36.19643"
+         style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:Arial;-inkscape-font-specification:'Arial Italic';stroke-width:0.26458332">AbstractSearch</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Italic';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       x="103.75446"
+       y="36.007439"
+       id="text4694"><tspan
+         sodipodi:role="line"
+         x="103.75446"
+         y="36.007439"
+         style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Italic';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="tspan4696">AbstractDovType</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Italic';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       x="171.22322"
+       y="36.007439"
+       id="text4702"><tspan
+         sodipodi:role="line"
+         id="tspan4700"
+         x="171.22322"
+         y="36.007439"
+         style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Italic';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1">AbstractDovSubType</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="35.151787"
+       y="58.119045"
+       id="text4746"><tspan
+         sodipodi:role="line"
+         x="35.151787"
+         y="58.119045"
+         style="stroke-width:0.26458332"
+         id="tspan4748">GrondwaterFilterSearch</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="103.56547"
+       y="57.74107"
+       id="text4754"><tspan
+         sodipodi:role="line"
+         id="tspan4752"
+         x="103.56547"
+         y="57.74107"
+         style="stroke-width:0.26458332">GrondwaterFilter</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="170.84525"
+       y="57.552082"
+       id="text4758"><tspan
+         sodipodi:role="line"
+         id="tspan4756"
+         x="170.84525"
+         y="57.552082"
+         style="stroke-width:0.26458332">Peilmeting</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL)"
+       d="m 35.907739,43.377977 v 4.913688"
+       id="path4760"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL-0)"
+       d="M 105.07738,43.494622 V 48.40831"
+       id="path4760-2"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL-3)"
+       d="m 171.60119,43.494621 v 4.913688"
+       id="path4760-28"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5907)"
+       d="M 61.988093,57.174105 H 75.973214"
+       id="path5833"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.23708406px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5907-7)"
+       d="m 131.71369,57.174105 h 11.22913"
+       id="path5833-5"
+       inkscape:connector-curvature="0" />
+    <path
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.11666656px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       d="m 63.034543,54.529306 c -0.03238,0.06546 -0.08751,0.132979 -0.165365,0.20257 -0.07786,0.06959 -0.168809,0.128847 -0.272851,0.177768 v 0.179835 c 0.05788,-0.02135 0.122988,-0.0534 0.195336,-0.09612 0.07304,-0.04272 0.131948,-0.08544 0.176734,-0.128157 v 1.185458 h 0.186034 v -1.521354 z"
+       id="text6590"
+       inkscape:connector-curvature="0" />
+    <path
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.11666656px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       d="m 75.376426,54.529306 c -0.03238,0.06546 -0.08751,0.132979 -0.165365,0.20257 -0.07786,0.06959 -0.168809,0.128847 -0.272851,0.177768 v 0.179835 c 0.05788,-0.02135 0.122988,-0.0534 0.195336,-0.09612 0.07304,-0.04272 0.131948,-0.08544 0.176734,-0.128157 v 1.185458 h 0.186034 v -1.521354 z"
+       id="text6590-9"
+       inkscape:connector-curvature="0" />
+    <path
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.11666656px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       d="m 132.45083,54.529306 c -0.0324,0.06546 -0.0875,0.132979 -0.16536,0.20257 -0.0779,0.06959 -0.16881,0.128847 -0.27286,0.177768 v 0.179835 c 0.0579,-0.02135 0.12299,-0.0534 0.19534,-0.09612 0.073,-0.04272 0.13195,-0.08544 0.17673,-0.128157 v 1.185458 h 0.18604 v -1.521354 z"
+       id="text6590-3"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222223px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="141.2686"
+       y="55.851189"
+       id="text6633"><tspan
+         sodipodi:role="line"
+         id="tspan6631"
+         x="141.2686"
+         y="55.851189"
+         style="font-size:2.11666656px;stroke-width:0.26458332">n</tspan></text>
+    <rect
+       style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27537987;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-3-5-3"
+       width="52.843773"
+       height="14.210013"
+       x="77.755524"
+       y="74.223053" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="103.56547"
+       y="82.500351"
+       id="text4754-7"><tspan
+         sodipodi:role="line"
+         id="tspan4752-5"
+         x="103.56547"
+         y="82.500351"
+         style="stroke-width:0.26458332">MyGrondwaterFilter</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL-0-9)"
+       d="m 105.07738,65.230093 v 7.937498"
+       id="path4760-2-0"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <rect
+       style="fill:#ffd5d5;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.27627483;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4543-3-2-6"
+       width="53.163059"
+       height="14.216631"
+       x="144.04947"
+       y="74.215988" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       x="170.84525"
+       y="82.02356"
+       id="text4758-1"><tspan
+         sodipodi:role="line"
+         id="tspan4756-1"
+         x="170.84525"
+         y="82.02356"
+         style="stroke-width:0.26458332">MyPeilmeting</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL-0-9-5)"
+       d="m 171.60119,65.230093 v 7.937498"
+       id="path4760-2-0-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5907-73)"
+       d="M 61.988093,57.174105 75.395235,81.330602"
+       id="path5833-1"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.11666656px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+       d="m 75.187438,77.580097 c -0.03238,0.06546 -0.08751,0.132979 -0.165365,0.20257 -0.07786,0.06959 -0.168809,0.128847 -0.272851,0.177768 v 0.179835 c 0.05788,-0.02135 0.122988,-0.0534 0.195336,-0.09612 0.07304,-0.04272 0.131948,-0.08544 0.176734,-0.128157 v 1.185458 h 0.186034 v -1.521354 z"
+       id="text6590-2"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g8694"
+       transform="translate(0.30210999,0.71567355)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path5833-5-8"
+         d="m 131.41158,81.624183 h 11.22913"
+         style="fill:none;stroke:#000000;stroke-width:0.23708408px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5907-7-7)" />
+      <path
+         inkscape:connector-curvature="0"
+         id="text6590-3-5"
+         d="m 132.14872,78.979384 c -0.0324,0.06546 -0.0875,0.132979 -0.16536,0.20257 -0.0779,0.06959 -0.16881,0.128847 -0.27286,0.177768 v 0.179835 c 0.0579,-0.02135 0.12299,-0.0534 0.19534,-0.09612 0.073,-0.04272 0.13195,-0.08544 0.17673,-0.128157 v 1.185458 h 0.18604 v -1.521354 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.11666656px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332" />
+      <text
+         id="text6633-3"
+         y="80.30127"
+         x="140.96649"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222223px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
+         xml:space="preserve"><tspan
+           style="font-size:2.11666656px;stroke-width:0.26458332"
+           y="80.30127"
+           x="140.96649"
+           id="tspan6631-2"
+           sodipodi:role="line">n</tspan></text>
+    </g>
+  </g>
+</svg>
diff --git a/docs/output_fields.rst b/docs/output_fields.rst
new file mode 100644
index 0000000..1b53b4a
--- /dev/null
+++ b/docs/output_fields.rst
@@ -0,0 +1,360 @@
+.. _output_df_fields:
+
+=======================
+Customizing data output
+=======================
+
+When using pydov to search datasets, the returned dataframe has different default columns (fields) depending on the dataset. We believe each dataframe to contain the most relevant fields for the corresponding dataset, but pydov allows you to select and customize the fields you want to be returned in the output dataframe.
+
+
+Using return fields
+*******************
+
+Next to the `query` and `location` parameters, you can use the ``return_fields`` parameter of the `search` method to limit the columns in the dataframe and/or specify extra columns not included by default. The `return_fields` parameter takes a list of field names, which can be any combination of the available fields for the dataset you're searching.
+
+For example, to query the boreholes in Ghent but only retrieve their depth, you'd use::
+
+  from pydov.search.boring import BoringSearch
+
+  bs = BoringSearch()
+  df = bs.search(query=PropertyIsEqualTo('gemeente', 'Gent'), return_fields=('pkey_boring', 'diepte_boring_tot'))
+
+
+Note that you can not only use ``return_fields`` to limit the columns from the default dataframe for you can also add extra fields not included in this default set. The following example returns the purpose ('doel') of all the Ghent boreholes::
+
+  from pydov.search.boring import BoringSearch
+
+  bs = BoringSearch()
+  df = bs.search(query=PropertyIsEqualTo('gemeente', 'Gent'), return_fields=('pkey_boring', 'doel'))
+
+
+You can get an overview of the available fields for a dataset using its search objects `get_fields` method. More information can be found in the :ref:`available_attribute_fields` section.
+
+.. note::
+
+    Significant performance gains can be achieved by only including the fields you need, and more specifically by including only fields with a cost of 1. More information can be found in the :ref:`performance` section below.
+
+    For instance, in the examples above only fields with a cost of 1 are selected, allowing the results to be retrieved almost instantly. By selecting only fields available in the WFS service (i.e. fields with a cost of 1), pydov only needs a single WFS query to obtain the results and doesn't need to download any additional XML documents.
+
+
+Defining custom object types
+****************************
+
+Should you want to make the returned dataframe fields more permanent or, more importantly, add extra XML fields to an existing object type, you can define your own object types and subtypes.
+
+pydov works internally with *search classes* (in pydov.search) and object *types* and *subtypes* (in pydov.types). The former are derived from :class:`pydov.search.abstract.AbstractSearch` and define the WFS services to be queried while the latter define which fields to retrieve from the WFS and XML services for inclusion in the resulting dataframe.
+
+An object main type (derived from :class:`pydov.types.abstract.AbstractDovType`, f.ex. GrondwaterFilter) can contain fields from both the WFS service as well as from the XML document, noting that there will be a single instance of the main type per WFS record. On the contrary, an object subtype (derived from :class:`pydov.types.abstract.AbstractDovSubType`, f.ex. Peilmeting) can list only fields from the XML document and can have a many-to-one relation with the main type: i.e. there can be multiple instances of the subtype for a given instance of the main type (f.ex. a single GrondwaterFilter can have multiple Peilmetingen). In the resulting output both will be combined in a single, flattened, dataframe whereby there will be as many rows as instances from the subtype, repeating the values of the main type for each one.
+
+.. figure:: objecttypes.svg
+   :alt: UML schema of search classes, object types and subtypes
+   :align: center
+
+   UML schema of search classes, object types and subtypes
+
+Search classes and object types are loosely coupled, each search class being linked to the default object type of the corresponding DOV object, allowing users to retrieve the default dataframe output when performing a search. However, to enable advanced customization of dataframe output columns at runtime, pydov allows for specifying an alternative object type upon creating an instance of the search classes. This system of 'pluggable types' enables users to extend the default type or subtype fields, or in fact rewrite them completely for their use-case.
+
+The three most common reasons to define custom types are listed below: adding an extra XML field to a main type, a subtype or defining a new custom subtype altogether.
+
+
+Adding an XML field to a main type
+----------------------------------
+
+To add an extra XML field to an existing main type, you have to create a subclass and extend the base type's fields.
+
+To extend the field list of the basetype, use its ``extend_fields`` class method, allowing the base object type to be unaffected by your changes. It takes a new list as its argument, containing the fields to be added. These should all be instances of :class:`pydov.types.fields.XmlField`. While it is possible to add instances of :class:`pydov.types.fields.WfsField` as well, this is generally not necessary as those can be used in the return_fields argument without being explicitly defined in the object type.
+
+For example, to add the field 'methode_xy' to the Boring datatype, you'd write::
+
+  from pydov.search.boring import BoringSearch
+  from pydov.types.boring import Boring
+  from pydov.types.fields import XmlField
+
+  class MyBoring(Boring):
+      fields = Boring.extend_fields([
+          XmlField(name='methode_xy',
+                   source_xpath='/boring/xy/methode_opmeten',
+                   datatype='string')
+      ])
+
+  bs = BoringSearch(objecttype=MyBoring)
+  df = bs.search(query=PropertyIsEqualTo('gemeente', 'Gent'))
+
+
+Adding an XML field to a subtype
+--------------------------------
+
+To add an extra XML field to an existing subtype, you have to create a subclass of the subtype and extend its fields. You also have to subclass the main type in order to register your new subtype.
+
+To extend the field list of the subtype, use its ``extend_fields`` class method, allowing the base subtype to be unaffected by your changes. It takes a new list as its argument, containing the fields to be added. These should all be instances of :class:`pydov.types.fields.XmlField`. The source_xpath will be interpreted relative to the base subtypes rootpath.
+
+To register your new subtype in a custom main type, subclass the existing main type and overwrite its ``subtypes`` field with a new list containing your new subtype.
+
+For example, to add the field 'opmeter' to the Peilmeting subtype, you'd write::
+
+  from pydov.search.grondwaterfilter import GrondwaterFilterSearch
+  from pydov.types.grondwaterfilter import GrondwaterFilter, Peilmeting
+  from pydov.types.fields import XmlField
+
+  class MyPeilmeting(Peilmeting):
+      fields = Peilmeting.extend_fields([
+          XmlField(name='opmeter',
+                   source_xpath='/opmeter/naam',
+                   datatype='string')
+      ])
+
+  class MyGrondwaterFilter(GrondwaterFilter):
+      subtypes = [MyPeilmeting]
+
+  fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilter)
+  df = fs.search(query=PropertyIsEqualTo('gemeente', 'Gent'))
+
+
+Adding a new subtype to a main type
+-----------------------------------
+
+To add a new subtype to an existing main type or, perhaps more likely, to replace the existing subtype of a main type, you have to specify the subtype and all of its fields. You also have to subclass the existing main type to register your subtype.
+
+Your new subtype should be a direct subclass of :class:`pydov.types.abstract.AbstractDovSubType` and should implement both the ``rootpath`` as well as the ``fields`` variables. The rootpath is the XPath expression of the root instances of this subtype in the XML document and should always start with ``.//``. There will be one instance of this subtype (and, consequently, one row in the output dataframe) for each element matched by this XPath expression.
+
+The fields should contain all the fields (or: columns in the output dataframe) of this new subtype. These should all be instances of :class:`pydov.types.fields.XmlField`. The source_xpath will be interpreted relative to the subtypes rootpath.
+
+Suppose you are not interested in the actual measurements from the CPT data but are instead interested in the different techniques applied while measuring. To get a dataframe with the different techniques per CPT location, you'd create a new subtype and register it in your own CPT type::
+
+  from pydov.search.sondering import SonderingSearch
+  from pydov.types.abstract import AbstractDovSubType
+  from pydov.types.sondering import Sondering
+  from pydov.types.fields import XmlField
+
+  class Technieken(AbstractDovSubType):
+
+      rootpath = './/sondering/sondeonderzoek/penetratietest/technieken'
+
+      fields = [
+          XmlField(name='techniek_diepte',
+                   source_xpath='/diepte_techniek',
+                   datatype='float'),
+          XmlField(name='techniek',
+                   source_xpath='/techniek',
+                   datatype='string')
+          XmlField(name='techniek_andere',
+                   source_xpath='/techniek_andere',
+                   datatype='string')
+      ]
+
+  class MySondering(Sondering)
+      subtypes = [Technieken]
+
+  ms = SonderingSearch(objecttype=MySondering)
+  df = ms.search(query=PropertyIsEqualTo('gemeente', 'Gent'))
+
+
+Default dataframe columns
+*************************
+
+Boreholes (boringen)
+--------------------
+  .. csv-table:: Boreholes (boringen)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1930-120730
+    boornummer,1,string,kb15d28w-B164
+    x,1,float,152301.0
+    y,1,float,211682.0
+    mv_mtaw,10,float,8.00
+    start_boring_mtaw,1,float,8.00
+    gemeente,1,string,Wuustwezel
+    diepte_boring_van,10,float,0.00
+    diepte_boring_tot,1,float,19.00
+    datum_aanvang,1,date,1930-10-01
+    uitvoerder,1,string,Smet - Dessel
+    boorgatmeting,10,boolean,false
+    diepte_methode_van,10,float,0.00
+    diepte_methode_tot,10,float,19.00
+    boormethode,10,string,droge boring
+
+CPT measurements (sonderingen)
+------------------------------
+  .. csv-table:: CPT measurements (sonderingen)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_sondering,1,string,https://www.dov.vlaanderen.be/data/sondering/2002-010317
+    x,1,float,142767
+    y,1,float,221907
+    start_sondering_mtaw,1,float,2.39
+    diepte_sondering_van,1,float,0
+    diepte_sondering_tot,1,float,16
+    datum_aanvang,1,date,2002-07-04
+    uitvoerder,1,string,MVG - Afdeling Geotechniek
+    sondeermethode,1,string,continu elektrisch
+    apparaat,1,string,200kN - RUPS
+    datum_gw_meting,10,datetime,2002-07-04 13:50:00
+    diepte_gw_m,10,float,1.2
+    z,10,float,1.2
+    qc,10,float,0.68
+    Qt,10,float,NaN
+    fs,10,float,10
+    u,10,float,7
+    i,10,float,0.1
+
+Groundwater screens (grondwaterfilters)
+---------------------------------------
+  .. csv-table:: Groundwater screens (grondwaterfilters)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_filter,1,string,https://www.dov.vlaanderen.be/data/filter/1989-001024
+    pkey_grondwaterlocatie,1,string,https://www.dov.vlaanderen.be/data/put/2017-000200
+    gw_id,1,string,4-0053
+    filternummer,1,string,1
+    filtertype,1,string,peilfilter
+    x,1,float,110490
+    y,1,float,194090
+    mv_mtaw,10,float,NaN
+    gemeente,1,string,Destelbergen
+    meetnet_code,10,integer,1
+    aquifer_code,10,string,0100
+    grondwaterlichaam_code,10,string,CVS_0160_GWL_1
+    regime,10,string,freatisch
+    diepte_onderkant_filter,1,float,13
+    lengte_filter,1,float,2
+    datum,10,date,2004-05-18
+    tijdstip,10,string,NaN
+    peil_mtaw,10,float,4.6
+    betrouwbaarheid,10,string,goed
+    methode,10,string,peillint
+    filterstatus,10,string,1
+    filtertoestand,10,string,in rust
+
+Formal stratigraphy (Formele stratigrafie)
+------------------------------------------
+  .. csv-table:: Formal stratigraphy (Formele stratigrafie)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2002-227082
+    pkey_boring,1,string,NaN
+    pkey_sondering,1,string,https://www.dov.vlaanderen.be/data/sondering/1989-068788
+    betrouwbaarheid_interpretatie,1,string,goed
+    x,1,float,108455
+    y,1,float,194565
+    diepte_laag_van,10,float,0
+    diepte_laag_tot,10,float,13
+    lid1,10,string,Q
+    relatie_lid1_lid2,10,string,T
+    lid2,10,string,Q
+
+Informal stratigraphy (Informele stratigrafie)
+----------------------------------------------
+  .. csv-table:: Informal stratigraphy (Informele stratigrafie)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2016-290843
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1893-073690
+    pkey_sondering,1,string,NaN
+    betrouwbaarheid_interpretatie,1,string,onbekend
+    x,1,float,108900
+    y,1,float,194425
+    diepte_laag_van,10,float,0
+    diepte_laag_tot,10,float,18.58
+    beschrijving,10,string,Q
+
+Hydrogeological stratigraphy (Hydrogeologische stratigrafie)
+------------------------------------------------------------
+  .. csv-table:: Hydrogeological stratigraphy (Hydrogeologische stratigrafie)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2001-198755
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1890-073688
+    betrouwbaarheid_interpretatie,1,string,goed
+    x,1,float,108773
+    y,1,float,194124
+    diepte_laag_van,10,float,0
+    diepte_laag_tot,10,float,8
+    aquifer,10,string,0110
+
+Coded lithology (Gecodeerde lithologie)
+---------------------------------------
+  .. csv-table:: Coded lithology (Gecodeerde lithologie)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2003-205091
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/2003-076348
+    betrouwbaarheid_interpretatie,1,string,goed
+    x,1,float,110601
+    y,1,float,196625
+    diepte_laag_van,10,float,4
+    diepte_laag_tot,10,float,4.5
+    hoofdnaam1_grondsoort,10,string,MZ
+    hoofdnaam2_grondsoort,10,string,NaN
+    bijmenging1_plaatselijk,10,boolean,False
+    bijmenging1_hoeveelheid,10,string,N
+    bijmenging1_grondsoort,10,string,SC
+    bijmenging2_plaatselijk,10,boolean,NaN
+    bijmenging2_hoeveelheid,10,string,NaN
+    bijmenging2_grondsoort,10,string,NaN
+    bijmenging3_plaatselijk,10,boolean,NaN
+    bijmenging3_hoeveelheid,10,string,NaN
+    bijmenging3_grondsoort,10,string,NaN
+
+Geotechnical encoding (Geotechnische codering)
+----------------------------------------------
+  .. csv-table:: Geotechnical encoding (Geotechnische codering)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2014-184535
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1957-033538
+    betrouwbaarheid_interpretatie,1,string,goed
+    x,1,float,108851
+    y,1,float,196510
+    diepte_laag_van,10,float,1
+    diepte_laag_tot,10,float,1.5
+    hoofdnaam1_grondsoort,10,string,XZ
+    hoofdnaam2_grondsoort,10,string,NaN
+    bijmenging1_plaatselijk,10,boolean,NaN
+    bijmenging1_hoeveelheid,10,string,NaN
+    bijmenging1_grondsoort,10,string,NaN
+    bijmenging2_plaatselijk,10,boolean,NaN
+    bijmenging2_hoeveelheid,10,string,NaN
+    bijmenging2_grondsoort,10,string,NaN
+    bijmenging3_plaatselijk,10,boolean,NaN
+    bijmenging3_hoeveelheid,10,string,NaN
+    bijmenging3_grondsoort,10,string,NaN
+
+Lithological descriptions (Lithologische beschrijvingen)
+--------------------------------------------------------
+  .. csv-table:: Lithological descriptions (Lithologische beschrijvingen)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2017-302166
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/2017-151410
+    betrouwbaarheid_interpretatie,1,string,onbekend
+    x,1,float,109491
+    y,1,float,196700
+    diepte_laag_van,10,float,0
+    diepte_laag_tot,10,float,1
+    beschrijving,10,string,klei/zand
+
+Quaternary stratigraphy (Quartaire stratigrafie)
+--------------------------------------------------------
+  .. csv-table:: Quaternary stratigraphy (Quartaire stratigrafie)
+    :header-rows: 1
+
+    Field,Cost,Datatype,Example
+    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/1999-057087
+    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1941-000322
+    betrouwbaarheid_interpretatie,1,string,onbekend
+    x,1,float,128277
+    y,1,float,178987
+    diepte_laag_van,10,float,0
+    diepte_laag_tot,10,float,8
+    lid1,10,string,F1
+    relatie_lid1_lid2,10,string,T
+    lid2,10,string,F1
diff --git a/docs/performance.rst b/docs/performance.rst
index b338546..fb58bf3 100644
--- a/docs/performance.rst
+++ b/docs/performance.rst
@@ -1,230 +1,3 @@
-.. _output_df_fields:
-
-=======================
-Output dataframe fields
-=======================
-
-When using pydov to search datasets, the returned dataframe has different default columns (fields) depending on the dataset. We believe each dataframe to contain the most relevant fields for the corresponding dataset, but pydov allows you to select the fields you want to be returned in the output dataframe.
-
-Next to the `query` and `location` parameters, you can use the ``return_fields`` parameter of the `search` method to limit the columns in the dataframe and/or specify extra columns not included by default. The `return_fields` parameter takes a list of field names, which can be any combination of the available fields for the dataset you're searching.
-
-You can get an overview of the available fields for a dataset using its search objects `get_fields` method. More information can be found in :ref:`available_attribute_fields`.
-
-.. note::
-
-    Significant performance gains can be achieved by only including the fields you need, and more specifically by including only fields with a cost of 1. More information can be found in the :ref:`performance` section below.
-
-
-Default dataframe columns
-*************************
-
-Boreholes (boringen)
---------------------
-  .. csv-table:: Boreholes (boringen)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1930-120730
-    boornummer,1,string,kb15d28w-B164
-    x,1,float,152301.0
-    y,1,float,211682.0
-    mv_mtaw,10,float,8.00
-    start_boring_mtaw,1,float,8.00
-    gemeente,1,string,Wuustwezel
-    diepte_boring_van,10,float,0.00
-    diepte_boring_tot,1,float,19.00
-    datum_aanvang,1,date,1930-10-01
-    uitvoerder,1,string,Smet - Dessel
-    boorgatmeting,10,boolean,false
-    diepte_methode_van,10,float,0.00
-    diepte_methode_tot,10,float,19.00
-    boormethode,10,string,droge boring
-
-CPT measurements (sonderingen)
-------------------------------
-  .. csv-table:: CPT measurements (sonderingen)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_sondering,1,string,https://www.dov.vlaanderen.be/data/sondering/2002-010317
-    x,1,float,142767
-    y,1,float,221907
-    start_sondering_mtaw,1,float,2.39
-    diepte_sondering_van,1,float,0
-    diepte_sondering_tot,1,float,16
-    datum_aanvang,1,date,2002-07-04
-    uitvoerder,1,string,MVG - Afdeling Geotechniek
-    sondeermethode,1,string,continu elektrisch
-    apparaat,1,string,200kN - RUPS
-    datum_gw_meting,10,datetime,2002-07-04 13:50:00
-    diepte_gw_m,10,float,1.2
-    z,10,float,1.2
-    qc,10,float,0.68
-    Qt,10,float,NaN
-    fs,10,float,10
-    u,10,float,7
-    i,10,float,0.1
-
-Groundwater screens (grondwaterfilters)
----------------------------------------
-  .. csv-table:: Groundwater screens (grondwaterfilters)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_filter,1,string,https://www.dov.vlaanderen.be/data/filter/1989-001024
-    pkey_grondwaterlocatie,1,string,https://www.dov.vlaanderen.be/data/put/2017-000200
-    gw_id,1,string,4-0053
-    filternummer,1,string,1
-    filtertype,1,string,peilfilter
-    x,1,float,110490
-    y,1,float,194090
-    mv_mtaw,10,float,NaN
-    gemeente,1,string,Destelbergen
-    meetnet_code,10,integer,1
-    aquifer_code,10,string,0100
-    grondwaterlichaam_code,10,string,CVS_0160_GWL_1
-    regime,10,string,freatisch
-    diepte_onderkant_filter,1,float,13
-    lengte_filter,1,float,2
-    datum,10,date,2004-05-18
-    tijdstip,10,string,NaN
-    peil_mtaw,10,float,4.6
-    betrouwbaarheid,10,string,goed
-    methode,10,string,peillint
-    filterstatus,10,string,1
-    filtertoestand,10,string,in rust
-
-Formal stratigraphy (Formele stratigrafie)
-------------------------------------------
-  .. csv-table:: Formal stratigraphy (Formele stratigrafie)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2002-227082
-    pkey_boring,1,string,NaN
-    pkey_sondering,1,string,https://www.dov.vlaanderen.be/data/sondering/1989-068788
-    betrouwbaarheid_interpretatie,1,string,goed
-    x,1,float,108455
-    y,1,float,194565
-    diepte_laag_van,10,float,0
-    diepte_laag_tot,10,float,13
-    lid1,10,string,Q
-    relatie_lid1_lid2,10,string,T
-    lid2,10,string,Q
-
-Informal stratigraphy (Informele stratigrafie)
-----------------------------------------------
-  .. csv-table:: Informal stratigraphy (Informele stratigrafie)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2016-290843
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1893-073690
-    pkey_sondering,1,string,NaN
-    betrouwbaarheid_interpretatie,1,string,onbekend
-    x,1,float,108900
-    y,1,float,194425
-    diepte_laag_van,10,float,0
-    diepte_laag_tot,10,float,18.58
-    beschrijving,10,string,Q
-
-Hydrogeological stratigraphy (Hydrogeologische stratigrafie)
-------------------------------------------------------------
-  .. csv-table:: Hydrogeological stratigraphy (Hydrogeologische stratigrafie)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2001-198755
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1890-073688
-    betrouwbaarheid_interpretatie,1,string,goed
-    x,1,float,108773
-    y,1,float,194124
-    diepte_laag_van,10,float,0
-    diepte_laag_tot,10,float,8
-    aquifer,10,string,0110
-
-Coded lithology (Gecodeerde lithologie)
----------------------------------------
-  .. csv-table:: Coded lithology (Gecodeerde lithologie)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2003-205091
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/2003-076348
-    betrouwbaarheid_interpretatie,1,string,goed
-    x,1,float,110601
-    y,1,float,196625
-    diepte_laag_van,10,float,4
-    diepte_laag_tot,10,float,4.5
-    hoofdnaam1_grondsoort,10,string,MZ
-    hoofdnaam2_grondsoort,10,string,NaN
-    bijmenging1_plaatselijk,10,boolean,False
-    bijmenging1_hoeveelheid,10,string,N
-    bijmenging1_grondsoort,10,string,SC
-    bijmenging2_plaatselijk,10,boolean,NaN
-    bijmenging2_hoeveelheid,10,string,NaN
-    bijmenging2_grondsoort,10,string,NaN
-    bijmenging3_plaatselijk,10,boolean,NaN
-    bijmenging3_hoeveelheid,10,string,NaN
-    bijmenging3_grondsoort,10,string,NaN
-
-Geotechnical encoding (Geotechnische codering)
-----------------------------------------------
-  .. csv-table:: Geotechnical encoding (Geotechnische codering)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2014-184535
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1957-033538
-    betrouwbaarheid_interpretatie,1,string,goed
-    x,1,float,108851
-    y,1,float,196510
-    diepte_laag_van,10,float,1
-    diepte_laag_tot,10,float,1.5
-    hoofdnaam1_grondsoort,10,string,XZ
-    hoofdnaam2_grondsoort,10,string,NaN
-    bijmenging1_plaatselijk,10,boolean,NaN
-    bijmenging1_hoeveelheid,10,string,NaN
-    bijmenging1_grondsoort,10,string,NaN
-    bijmenging2_plaatselijk,10,boolean,NaN
-    bijmenging2_hoeveelheid,10,string,NaN
-    bijmenging2_grondsoort,10,string,NaN
-    bijmenging3_plaatselijk,10,boolean,NaN
-    bijmenging3_hoeveelheid,10,string,NaN
-    bijmenging3_grondsoort,10,string,NaN
-
-Lithological descriptions (Lithologische beschrijvingen)
---------------------------------------------------------
-  .. csv-table:: Lithological descriptions (Lithologische beschrijvingen)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/2017-302166
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/2017-151410
-    betrouwbaarheid_interpretatie,1,string,onbekend
-    x,1,float,109491
-    y,1,float,196700
-    diepte_laag_van,10,float,0
-    diepte_laag_tot,10,float,1
-    beschrijving,10,string,klei/zand
-
-Quaternary stratigraphy (Quartaire stratigrafie)
---------------------------------------------------------
-  .. csv-table:: Quaternary stratigraphy (Quartaire stratigrafie)
-    :header-rows: 1
-
-    Field,Cost,Datatype,Example
-    pkey_interpretatie,1,string,https://www.dov.vlaanderen.be/data/interpretatie/1999-057087
-    pkey_boring,1,string,https://www.dov.vlaanderen.be/data/boring/1941-000322
-    betrouwbaarheid_interpretatie,1,string,onbekend
-    x,1,float,128277
-    y,1,float,178987
-    diepte_laag_van,10,float,0
-    diepte_laag_tot,10,float,8
-    lid1,10,string,F1
-    relatie_lid1_lid2,10,string,T
-    lid2,10,string,F1
-
 .. _performance:
 
 ===========
diff --git a/docs/reference.rst b/docs/reference.rst
index a4f36e3..0d7933a 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -4,7 +4,7 @@ API reference
 Search classes
 --------------
 
-.. .. automodule:: pydov.search.abstract
+.. automodule:: pydov.search.abstract
     :members:
 
 Boring
@@ -34,7 +34,7 @@ Interpretaties
 Object types
 ------------
 
-.. .. automodule:: pydov.types.abstract
+.. automodule:: pydov.types.abstract
     :members:
 
 Boring
@@ -65,6 +65,14 @@ Interpretaties
     :members:
     :show-inheritance:
 
+Fields
+------
+
+.. automodule:: pydov.types.fields
+    :members:
+    :show-inheritance:
+
+
 Search utilities
 ----------------
 
diff --git a/docs/tutorials.rst b/docs/tutorials.rst
index 9478324..d30c4bc 100644
--- a/docs/tutorials.rst
+++ b/docs/tutorials.rst
@@ -31,4 +31,5 @@ To run these interactively online without installation, use the following binder
    notebooks/search_gecodeerde_lithologie.ipynb
    notebooks/search_geotechnische_codering.ipynb
    notebooks/search_quartaire_stratigrafie.ipynb
+   notebooks/customizing_object_types.ipynb
    notebooks/caching.ipynb
diff --git a/pydov/search/abstract.py b/pydov/search/abstract.py
index 8db0be7..b40e02d 100644
--- a/pydov/search/abstract.py
+++ b/pydov/search/abstract.py
@@ -311,11 +311,30 @@ class AbstractSearch(AbstractCommon):
                 The cost associated with the request of this field in the
                 output dataframe.
 
+        Raises
+        ------
+        RuntimeError
+            When the defined fields of this type are invalid.
+
         """
         fields = {}
         self._wfs_fields = []
         self._geometry_column = wfs_schema.get('geometry_column', None)
 
+        for f in self._type.get_fields(include_subtypes=False).values():
+            if not isinstance(f, pydov.types.fields.AbstractField):
+                raise RuntimeError(
+                    "Type '{}' fields should be instances of "
+                    "pydov.types.fields.AbstractField, found {}.".format(
+                        self._type.__name__, str(type(f))))
+
+        for f in self._type.get_fields(include_subtypes=True).values():
+            if not isinstance(f, pydov.types.fields.AbstractField):
+                raise RuntimeError(
+                    "Fields of subtype of '{}' should be instances of "
+                    "pydov.types.fields.AbstractField, found {}.".format(
+                        self._type.__name__, str(type(f))))
+
         _map_wfs_datatypes = {
             'int': 'integer',
             'decimal': 'float',
diff --git a/pydov/search/boring.py b/pydov/search/boring.py
index 23a5d3d..6862898 100644
--- a/pydov/search/boring.py
+++ b/pydov/search/boring.py
@@ -2,6 +2,7 @@
 """Module containing the search classes to retrieve DOV borehole data."""
 import pandas as pd
 
+from pydov.types.fields import _WfsInjectedField
 from .abstract import AbstractSearch
 from ..types.boring import Boring
 from ..util import owsutil
@@ -16,9 +17,18 @@ class BoringSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
-        super(BoringSearch, self).__init__('dov-pub:Boringen', Boring)
+    def __init__(self, objecttype=Boring):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the Boring type.
+            Optional: defaults to the Boring type containing the fields
+            described in the documentation.
+
+        """
+        super(BoringSearch, self).__init__('dov-pub:Boringen', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -55,13 +65,9 @@ class BoringSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 BoringSearch.__wfs_schema,
@@ -121,8 +127,9 @@ class BoringSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        boringen = Boring.from_wfs(fts, self.__wfs_namespace)
+        boringen = self._type.from_wfs(fts, self.__wfs_namespace)
 
-        df = pd.DataFrame(data=Boring.to_df_array(boringen, return_fields),
-                          columns=Boring.get_field_names(return_fields))
+        df = pd.DataFrame(
+            data=self._type.to_df_array(boringen, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
diff --git a/pydov/search/grondwaterfilter.py b/pydov/search/grondwaterfilter.py
index 67bdf1c..a89e313 100644
--- a/pydov/search/grondwaterfilter.py
+++ b/pydov/search/grondwaterfilter.py
@@ -7,6 +7,7 @@ from owslib.fes import (
     PropertyIsNull,
     And,
 )
+from pydov.types.fields import _WfsInjectedField
 from .abstract import AbstractSearch
 from ..types.grondwaterfilter import GrondwaterFilter
 from ..util import owsutil
@@ -23,10 +24,19 @@ class GrondwaterFilterSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=GrondwaterFilter):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the GrondwaterFilter type.
+            Optional: defaults to the GrondwaterFilter type containing the
+            fields described in the documentation.
+
+        """
         super(GrondwaterFilterSearch,
-              self).__init__('gw_meetnetten:meetnetten', GrondwaterFilter)
+              self).__init__('gw_meetnetten:meetnetten', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -64,13 +74,9 @@ class GrondwaterFilterSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 GrondwaterFilterSearch.__wfs_schema,
@@ -144,10 +150,10 @@ class GrondwaterFilterSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        gw_filters = GrondwaterFilter.from_wfs(fts, self.__wfs_namespace)
+        gw_filters = self._type.from_wfs(fts, self.__wfs_namespace)
+
+        df = pd.DataFrame(
+            data=self._type.to_df_array(gw_filters, return_fields),
+            columns=self._type.get_field_names(return_fields))
 
-        df = pd.DataFrame(data=GrondwaterFilter.to_df_array(gw_filters,
-                                                            return_fields),
-                          columns=GrondwaterFilter.get_field_names(
-                              return_fields))
         return df
diff --git a/pydov/search/interpretaties.py b/pydov/search/interpretaties.py
index a68dc89..abec840 100644
--- a/pydov/search/interpretaties.py
+++ b/pydov/search/interpretaties.py
@@ -1,6 +1,7 @@
 import pandas as pd
 
 from pydov.search.abstract import AbstractSearch
+from pydov.types.fields import _WfsInjectedField
 from pydov.types.interpretaties import FormeleStratigrafie
 from pydov.types.interpretaties import InformeleStratigrafie
 from pydov.types.interpretaties import HydrogeologischeStratigrafie
@@ -20,10 +21,19 @@ class InformeleStratigrafieSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=InformeleStratigrafie):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the InformeleStratigrafie type.
+            Optional: defaults to the InformeleStratigrafie type
+            containing the fields described in the documentation.
+
+        """
         super(InformeleStratigrafieSearch, self).__init__(
-            'interpretaties:informele_stratigrafie', InformeleStratigrafie)
+            'interpretaties:informele_stratigrafie', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -61,13 +71,9 @@ class InformeleStratigrafieSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 InformeleStratigrafieSearch.__wfs_schema,
@@ -128,13 +134,12 @@ class InformeleStratigrafieSearch(AbstractSearch):
                            return_fields=return_fields,
                            extra_wfs_fields=['Type_proef', 'Proeffiche'])
 
-        interpretaties = InformeleStratigrafie.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=InformeleStratigrafie.to_df_array(
-                interpretaties, return_fields),
-            columns=InformeleStratigrafie.get_field_names(return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -148,10 +153,19 @@ class FormeleStratigrafieSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=FormeleStratigrafie):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the FormeleStratigrafie type.
+            Optional: defaults to the FormeleStratigrafie type containing the
+            fields described in the documentation.
+
+        """
         super(FormeleStratigrafieSearch, self).__init__(
-            'interpretaties:formele_stratigrafie', FormeleStratigrafie)
+            'interpretaties:formele_stratigrafie', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -189,13 +203,9 @@ class FormeleStratigrafieSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 FormeleStratigrafieSearch.__wfs_schema,
@@ -256,13 +266,12 @@ class FormeleStratigrafieSearch(AbstractSearch):
                            return_fields=return_fields,
                            extra_wfs_fields=['Type_proef', 'Proeffiche'])
 
-        interpretaties = FormeleStratigrafie.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=FormeleStratigrafie.to_df_array(
-                interpretaties, return_fields),
-            columns=FormeleStratigrafie.get_field_names(return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -275,11 +284,20 @@ class HydrogeologischeStratigrafieSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=HydrogeologischeStratigrafie):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the HydrogeologischeStratigrafie
+            type. Optional: defaults to the HydrogeologischeStratigrafie type
+            containing the fields described in the documentation.
+
+        """
         super(HydrogeologischeStratigrafieSearch, self).__init__(
             'interpretaties:hydrogeologische_stratigrafie',
-            HydrogeologischeStratigrafie)
+            objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -320,13 +338,9 @@ class HydrogeologischeStratigrafieSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 HydrogeologischeStratigrafieSearch.__wfs_schema,
@@ -387,14 +401,12 @@ class HydrogeologischeStratigrafieSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        interpretaties = HydrogeologischeStratigrafie.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=HydrogeologischeStratigrafie.to_df_array(
-                interpretaties, return_fields),
-            columns=HydrogeologischeStratigrafie.get_field_names(
-                return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -407,11 +419,20 @@ class LithologischeBeschrijvingenSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=LithologischeBeschrijvingen):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the LithologischeBeschrijvingen
+            type. Optional: defaults to the LithologischeBeschrijvingen type
+            containing the fields described in the documentation.
+
+        """
         super(LithologischeBeschrijvingenSearch, self).__init__(
             'interpretaties:lithologische_beschrijvingen',
-            LithologischeBeschrijvingen)
+            objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -452,13 +473,9 @@ class LithologischeBeschrijvingenSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 LithologischeBeschrijvingenSearch.__wfs_schema,
@@ -519,14 +536,12 @@ class LithologischeBeschrijvingenSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        interpretaties = LithologischeBeschrijvingen.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=LithologischeBeschrijvingen.to_df_array(
-                interpretaties, return_fields),
-            columns=LithologischeBeschrijvingen.get_field_names(
-                return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -539,11 +554,20 @@ class GecodeerdeLithologieSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=GecodeerdeLithologie):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the GecodeerdeLithologie type.
+            Optional: defaults to the GecodeerdeLithologie type containing
+            the fields described in the documentation.
+
+        """
         super(GecodeerdeLithologieSearch, self).__init__(
             'interpretaties:gecodeerde_lithologie',
-            GecodeerdeLithologie)
+            objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -584,13 +608,9 @@ class GecodeerdeLithologieSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 GecodeerdeLithologieSearch.__wfs_schema,
@@ -651,14 +671,12 @@ class GecodeerdeLithologieSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        interpretaties = GecodeerdeLithologie.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=GecodeerdeLithologie.to_df_array(
-                interpretaties, return_fields),
-            columns=GecodeerdeLithologie.get_field_names(
-                return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -671,11 +689,20 @@ class GeotechnischeCoderingSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=GeotechnischeCodering):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the GeotechnischeCodering type.
+            Optional: defaults to the GeotechnischeCodering type containing
+            the fields described in the documentation.
+
+        """
         super(GeotechnischeCoderingSearch, self).__init__(
             'interpretaties:geotechnische_coderingen',
-            GeotechnischeCodering)
+            objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -716,13 +743,9 @@ class GeotechnischeCoderingSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 GeotechnischeCoderingSearch.__wfs_schema,
@@ -779,14 +802,12 @@ class GeotechnischeCoderingSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        interpretaties = GeotechnischeCodering.from_wfs(
+        interpretaties = self._type.from_wfs(
             fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=GeotechnischeCodering.to_df_array(
-                interpretaties, return_fields),
-            columns=GeotechnischeCodering.get_field_names(
-                return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
 
 
@@ -800,10 +821,19 @@ class QuartairStratigrafieSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
+    def __init__(self, objecttype=QuartairStratigrafie):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the QuartairStratigrafie type.
+            Optional: defaults to the QuartairStratigrafie type containing
+            the fields described in the documentation.
+
+        """
         super(QuartairStratigrafieSearch, self).__init__(
-            'interpretaties:quartaire_stratigrafie', QuartairStratigrafie)
+            'interpretaties:quartaire_stratigrafie', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -841,13 +871,9 @@ class QuartairStratigrafieSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 QuartairStratigrafieSearch.__wfs_schema,
@@ -909,11 +935,9 @@ class QuartairStratigrafieSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        interpretaties = QuartairStratigrafie.from_wfs(
-            fts, self.__wfs_namespace)
+        interpretaties = self._type.from_wfs(fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=QuartairStratigrafie.to_df_array(
-                interpretaties, return_fields),
-            columns=QuartairStratigrafie.get_field_names(return_fields))
+            data=self._type.to_df_array(interpretaties, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
diff --git a/pydov/search/sondering.py b/pydov/search/sondering.py
index e1c37d1..7561f0e 100644
--- a/pydov/search/sondering.py
+++ b/pydov/search/sondering.py
@@ -3,6 +3,7 @@
 import pandas as pd
 
 from pydov.search.abstract import AbstractSearch
+from pydov.types.fields import _WfsInjectedField
 from pydov.types.sondering import Sondering
 from pydov.util import owsutil
 
@@ -17,9 +18,19 @@ class SonderingSearch(AbstractSearch):
     __fc_featurecatalogue = None
     __xsd_schemas = None
 
-    def __init__(self):
-        """Initialisation."""
-        super(SonderingSearch, self).__init__('dov-pub:Sonderingen', Sondering)
+    def __init__(self, objecttype=Sondering):
+        """Initialisation.
+
+        Parameters
+        ----------
+        objecttype : subclass of pydov.types.abstract.AbstractDovType
+            Reference to a class representing the Sondering type.
+            Optional: defaults to the Sondering type containing
+            the fields described in the documentation.
+
+        """
+        super(SonderingSearch, self).__init__(
+            'dov-pub:Sonderingen', objecttype)
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer."""
@@ -56,13 +67,9 @@ class SonderingSearch(AbstractSearch):
             for field in fields.values():
                 if field['name'] not in self._type.get_field_names(
                         include_wfs_injected=True):
-                    self._type._fields.append({
-                        'name': field['name'],
-                        'source': 'wfs',
-                        'sourcefield': field['name'],
-                        'type': field['type'],
-                        'wfs_injected': True
-                    })
+                    self._type.fields.append(
+                        _WfsInjectedField(name=field['name'],
+                                          datatype=field['type']))
 
             self._fields = self._build_fields(
                 SonderingSearch.__wfs_schema,
@@ -122,9 +129,9 @@ class SonderingSearch(AbstractSearch):
         fts = self._search(location=location, query=query,
                            return_fields=return_fields)
 
-        sonderingen = Sondering.from_wfs(fts, self.__wfs_namespace)
+        sonderingen = self._type.from_wfs(fts, self.__wfs_namespace)
 
         df = pd.DataFrame(
-            data=Sondering.to_df_array(sonderingen, return_fields),
-            columns=Sondering.get_field_names(return_fields))
+            data=self._type.to_df_array(sonderingen, return_fields),
+            columns=self._type.get_field_names(return_fields))
         return df
diff --git a/pydov/types/abstract.py b/pydov/types/abstract.py
index 2a5eb0a..634f4eb 100644
--- a/pydov/types/abstract.py
+++ b/pydov/types/abstract.py
@@ -11,6 +11,7 @@ import numpy as np
 
 from owslib.etree import etree
 from pydov.search.abstract import AbstractCommon
+from pydov.types.fields import AbstractField
 from pydov.util.dovutil import (
     get_dov_xml,
     parse_dov_xml,
@@ -25,7 +26,16 @@ from ..util.errors import (
 
 class AbstractTypeCommon(AbstractCommon):
     """Class grouping methods common to AbstractDovType and
-    AbstractDovSubType."""
+    AbstractDovSubType.
+
+    Attributes
+    ----------
+    fields : list of pydov.types.fields.AbstractField
+        List of fields of this type.
+
+    """
+
+    fields = []
 
     @classmethod
     def _parse(cls, func, xpath, namespace, returntype):
@@ -63,16 +73,48 @@ class AbstractTypeCommon(AbstractCommon):
 
         return cls._typeconvert(text, returntype)
 
+    @classmethod
+    def extend_fields(cls, extra_fields):
+        """Extend the fields of this type with given extra fields and return
+        the new fieldset.
+
+        Parameters
+        ----------
+        extra_fields : list of pydov.types.fields.AbstractField
+            Extra fields to be appended to the existing fields of this type.
+
+        Returns
+        -------
+        list of pydov.types.fields.AbstractField
+            List of the existing fields of this type, extended with the
+            extra fields supplied in extra_fields.
+
+        """
+        fields = list(cls.fields)
+        fields.extend(extra_fields)
+        return fields
+
 
 class AbstractDovSubType(AbstractTypeCommon):
+    """Abstract DOV type grouping fields and methods common to all DOV
+    subtypes. Not to be instantiated or used directly.
 
-    _name = None
-    _rootpath = None
+    Attributes
+    ----------
+    rootpath : str
+        XPath expression of the root element of this subtype. Should return
+        all elements of this subtype.
 
-    _UNRESOLVED = "{UNRESOLVED}"
-    _fields = []
+    Raises
+    ------
+    RuntimeError
+        When the defined fields of this type are invalid.
+
+    """
+
+    rootpath = None
 
-    _xsd_schemas = []
+    _UNRESOLVED = "{UNRESOLVED}"
 
     def __init__(self):
         """Initialisation.
@@ -83,6 +125,13 @@ class AbstractDovSubType(AbstractTypeCommon):
             The name associated with this subtype.
 
         """
+        for f in self.fields:
+            if not isinstance(f, AbstractField):
+                raise RuntimeError(
+                    "Subtype '{}' fields should be instances of "
+                    "pydov.types.fields.AbstractField, found {}.".format(
+                        self.__class__.__name__, str(type(f))))
+
         self.data = dict(
             zip(self.get_field_names(),
                 [AbstractDovSubType._UNRESOLVED] * len(self.get_field_names()))
@@ -106,7 +155,7 @@ class AbstractDovSubType(AbstractTypeCommon):
         """
         try:
             tree = parse_dov_xml(xml_data)
-            for element in tree.findall(cls._rootpath):
+            for element in tree.findall(cls.rootpath):
                 yield cls.from_xml_element(element)
         except XmlParseError:
             # Ignore XmlParseError here in subtypes, assuming it will be
@@ -153,7 +202,7 @@ class AbstractDovSubType(AbstractTypeCommon):
             the names of the columns in the output dataframe for this type.
 
         """
-        return [f['name'] for f in cls._fields]
+        return [f['name'] for f in cls.fields]
 
     @classmethod
     def get_fields(cls):
@@ -189,8 +238,8 @@ class AbstractDovSubType(AbstractTypeCommon):
 
         """
         return OrderedDict(
-            zip([f['name'] for f in cls._fields],
-                [f for f in cls._fields]))
+            zip([f['name'] for f in cls.fields],
+                [f for f in cls.fields]))
 
     @classmethod
     def get_name(cls):
@@ -202,31 +251,23 @@ class AbstractDovSubType(AbstractTypeCommon):
             The name associated with this subtype.
 
         """
-        return cls._name
-
-    @classmethod
-    def get_root_path(cls):
-        """Return the root XPath of the XML element of this subtype.
-
-        Returns
-        -------
-        xpath : str
-            The XPath of the XML root element of this subtype.
-
-        """
-        return cls._rootpath
+        return cls.__name__
 
 
 class AbstractDovType(AbstractTypeCommon):
     """Abstract DOV type grouping fields and methods common to all DOV
-    object types. Not to be instantiated or used directly."""
+    object types. Not to be instantiated or used directly.
 
-    _subtypes = []
+    Attributes
+    ----------
+    subtypes : list of subclass of pydov.types.abstract.AbstractDovSubType
+        List of subtypes of this type.
 
-    _UNRESOLVED = "{UNRESOLVED}"
-    _fields = []
+    """
 
-    _xsd_schemas = []
+    subtypes = []
+
+    _UNRESOLVED = "{UNRESOLVED}"
 
     def __init__(self, typename, pkey):
         """Initialisation.
@@ -239,6 +280,11 @@ class AbstractDovType(AbstractTypeCommon):
             Permanent key of this DOV object, being a URI of the form
             `https://www.dov.vlaanderen.be/data/typename/id`.
 
+        Raises
+        ------
+        RuntimeError
+            When the defined fields of this type are invalid.
+
         """
         if typename is None or pkey is None:
             raise ValueError(
@@ -249,14 +295,21 @@ class AbstractDovType(AbstractTypeCommon):
         self.typename = typename
         self.pkey = pkey
 
+        for f in self.fields:
+            if not isinstance(f, AbstractField):
+                raise RuntimeError(
+                    "Type '{}' fields should be instances of "
+                    "pydov.types.fields.AbstractField, found {}.".format(
+                        self.__class__.__name__, str(type(f))))
+
         self.data = dict(
             zip(self.get_field_names(include_subtypes=False),
                 [AbstractDovType._UNRESOLVED] * len(self.get_field_names()))
         )
 
         self.subdata = dict(
-            zip([st.get_name() for st in self._subtypes],
-                [] * len(self._subtypes))
+            zip([st.get_name() for st in self.subtypes],
+                [] * len(self.subtypes))
         )
 
         self.data['pkey_{}'.format(self.typename)] = self.pkey
@@ -392,21 +445,21 @@ class AbstractDovType(AbstractTypeCommon):
         """
         if return_fields is None:
             if include_wfs_injected:
-                fields = [f['name'] for f in cls._fields]
+                fields = [f['name'] for f in cls.fields]
             else:
-                fields = [f['name'] for f in cls._fields if not f.get(
+                fields = [f['name'] for f in cls.fields if not f.get(
                     'wfs_injected', False)]
             if include_subtypes:
-                for st in cls._subtypes:
+                for st in cls.subtypes:
                     fields.extend(st.get_field_names())
         elif type(return_fields) not in (list, tuple, set):
             raise AttributeError(
                 'return_fields should be a list, tuple or set')
         else:
-            fields = [f['name'] for f in cls._fields if f['name'] in
+            fields = [f['name'] for f in cls.fields if f['name'] in
                       return_fields]
             if include_subtypes:
-                for st in cls._subtypes:
+                for st in cls.subtypes:
                     fields.extend([f for f in st.get_field_names() if f in
                                    return_fields])
             for rf in return_fields:
@@ -465,11 +518,11 @@ class AbstractDovType(AbstractTypeCommon):
 
         """
         fields = OrderedDict(
-            zip([f['name'] for f in cls._fields if f['source'] in source],
-                [f for f in cls._fields if f['source'] in source]))
+            zip([f['name'] for f in cls.fields if f['source'] in source],
+                [f for f in cls.fields if f['source'] in source]))
 
         if include_subtypes and 'xml' in source:
-            for st in cls._subtypes:
+            for st in cls.subtypes:
                 fields.update(st.get_fields())
 
         return fields
@@ -486,12 +539,12 @@ class AbstractDovType(AbstractTypeCommon):
 
         """
         xsd_schemas = set()
-        for s in cls._xsd_schemas:
-            xsd_schemas.add(s)
 
-        for st in cls._subtypes:
-            for s in st._xsd_schemas:
-                xsd_schemas.add(s)
+        fields = cls.get_fields(source='xml', include_subtypes=True)
+
+        for f in fields.values():
+            if 'xsd_type' in f:
+                xsd_schemas.add(f['xsd_schema'])
 
         return xsd_schemas
 
@@ -575,7 +628,7 @@ class AbstractDovType(AbstractTypeCommon):
             The raw XML data of the DOV object as bytes.
 
         """
-        for subtype in self._subtypes:
+        for subtype in self.subtypes:
             st_name = subtype.get_name()
             if st_name not in self.subdata:
                 self.subdata[st_name] = []
diff --git a/pydov/types/boring.py b/pydov/types/boring.py
index 6baab94..cbf1433 100644
--- a/pydov/types/boring.py
+++ b/pydov/types/boring.py
@@ -1,7 +1,10 @@
 # -*- coding: utf-8 -*-
 """Module containing the DOV data type for boreholes (Boring), including
 subtypes."""
-
+from pydov.types.fields import (
+    XmlField,
+    WfsField,
+)
 from .abstract import (
     AbstractDovType,
     AbstractDovSubType,
@@ -10,108 +13,61 @@ from .abstract import (
 
 class BoorMethode(AbstractDovSubType):
 
-    _name = 'boormethode'
-    _rootpath = './/boring/details/boormethode'
-
-    _fields = [{
-        'name': 'diepte_methode_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Bovenkant van de laag die met een bepaalde '
-                      'methode aangeboord werd, in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_methode_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Onderkant van de laag die met een bepaalde '
-                      'methode aangeboord werd, in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'boormethode',
-        'source': 'xml',
-        'sourcefield': '/methode',
-        'definition': 'Boormethode voor het diepte-interval.',
-        'type': 'string',
-        'notnull': False
-    }]
+    rootpath = './/boring/details/boormethode'
+
+    fields = [
+        XmlField(name='diepte_methode_van',
+                 source_xpath='/van',
+                 definition='Bovenkant van de laag die met een bepaalde '
+                            'methode aangeboord werd, in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_methode_tot',
+                 source_xpath='/tot',
+                 definition='Onderkant van de laag die met een bepaalde '
+                            'methode aangeboord werd, in meter.',
+                 datatype='float'),
+        XmlField(name='boormethode',
+                 source_xpath='/methode',
+                 definition='Boormethode voor het diepte-interval.',
+                 datatype='string')
+    ]
 
 
 class Boring(AbstractDovType):
     """Class representing the DOV data type for boreholes."""
 
-    _subtypes = [BoorMethode]
-
-    _fields = [{
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'sourcefield': 'fiche',
-        'type': 'string'
-    }, {
-        'name': 'boornummer',
-        'source': 'wfs',
-        'sourcefield': 'boornummer',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }, {
-        'name': 'mv_mtaw',
-        'source': 'xml',
-        'sourcefield': '/boring/oorspronkelijk_maaiveld/waarde',
-        'definition': 'Maaiveldhoogte in mTAW op dag dat de boring '
-                      'uitgevoerd werd.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'start_boring_mtaw',
-        'source': 'wfs',
-        'sourcefield': 'Z_mTAW',
-        'type': 'float'
-    }, {
-        'name': 'gemeente',
-        'source': 'wfs',
-        'sourcefield': 'gemeente',
-        'type': 'string'
-    }, {
-        'name': 'diepte_boring_van',
-        'source': 'xml',
-        'sourcefield': '/boring/diepte_van',
-        'definition': 'Startdiepte van de boring (in meter).',
-        'type': 'float',
-        'notnull': True
-    }, {
-        'name': 'diepte_boring_tot',
-        'source': 'wfs',
-        'sourcefield': 'diepte_tot_m',
-        'type': 'float'
-    }, {
-        'name': 'datum_aanvang',
-        'source': 'wfs',
-        'sourcefield': 'datum_aanvang',
-        'type': 'date'
-    }, {
-        'name': 'uitvoerder',
-        'source': 'wfs',
-        'sourcefield': 'uitvoerder',
-        'type': 'string'
-    }, {
-        'name': 'boorgatmeting',
-        'source': 'xml',
-        'sourcefield': '/boring/boorgatmeting/uitgevoerd',
-        'definition': 'Is er een boorgatmeting uitgevoerd (ja/nee).',
-        'type': 'boolean',
-        'notnull': False
-    }]
+    subtypes = [BoorMethode]
+
+    fields = [
+        WfsField(name='pkey_boring', source_field='fiche', datatype='string'),
+        WfsField(name='boornummer', source_field='boornummer',
+                 datatype='string'),
+        WfsField(name='x', source_field='X_mL72', datatype='float'),
+        WfsField(name='y', source_field='Y_mL72', datatype='float'),
+        XmlField(name='mv_mtaw',
+                 source_xpath='/boring/oorspronkelijk_maaiveld/waarde',
+                 definition='Maaiveldhoogte in mTAW op dag dat de boring '
+                            'uitgevoerd werd.',
+                 datatype='float'),
+        WfsField(name='start_boring_mtaw', source_field='Z_mTAW',
+                 datatype='float'),
+        WfsField(name='gemeente', source_field='gemeente', datatype='string'),
+        XmlField(name='diepte_boring_van',
+                 source_xpath='/boring/diepte_van',
+                 definition='Startdiepte van de boring (in meter).',
+                 datatype='float',
+                 notnull=True),
+        WfsField(name='diepte_boring_tot', source_field='diepte_tot_m',
+                 datatype='float'),
+        WfsField(name='datum_aanvang', source_field='datum_aanvang',
+                 datatype='date'),
+        WfsField(name='uitvoerder', source_field='uitvoerder',
+                 datatype='string'),
+        XmlField(name='boorgatmeting',
+                 source_xpath='/boring/boorgatmeting/uitgevoerd',
+                 definition='Is er een boorgatmeting uitgevoerd (ja/nee).',
+                 datatype='boolean')
+    ]
 
     def __init__(self, pkey):
         """Initialisation.
@@ -143,7 +99,7 @@ class Boring(AbstractDovType):
             element.
 
         """
-        b = Boring(feature.findtext('./{{{}}}fiche'.format(namespace)))
+        b = cls(feature.findtext('./{{{}}}fiche'.format(namespace)))
 
         for field in cls.get_fields(source=('wfs',)).values():
             b.data[field['name']] = cls._parse(
diff --git a/pydov/types/fields.py b/pydov/types/fields.py
new file mode 100644
index 0000000..1c78025
--- /dev/null
+++ b/pydov/types/fields.py
@@ -0,0 +1,148 @@
+"""Module grouping all classes related to pydov field definitions."""
+
+
+class XsdType(object):
+    """Class for specifying an XSD type from an XSD schema. This will be
+    resolved at runtime in a list of possible values and their definitions."""
+
+    def __init__(self, xsd_schema, typename):
+        """Initialise a XSD type reference.
+
+        Parameters
+        ----------
+        xsd_schema : str
+            URL of XSD schema record containing the specified typename.
+        typename : str
+            Name of the type.
+
+        """
+        self.xsd_schema = xsd_schema
+        self.typename = typename
+
+
+class AbstractField(dict):
+    """Abstract base class for pydov field definitions. Not to be
+    instantiated directly."""
+
+    def __init__(self, name, source, datatype, **kwargs):
+        """Initialise a field.
+
+        Parameters
+        ----------
+        name : str
+            Name of this field in the return dataframe.
+        source: one of 'wfs', 'xml', 'custom'
+            Source of this field.
+        datatype : one of 'string', 'integer', 'float', 'date', 'datetime'
+                or 'boolean'
+            Datatype of the values of this field in the return dataframe.
+
+        """
+        super(AbstractField, self).__init__(**kwargs)
+        self.__setitem__('name', name)
+        self.__setitem__('source', source)
+        self.__setitem__('type', datatype)
+
+
+class WfsField(AbstractField):
+    """Class for a field available in the WFS service."""
+
+    def __init__(self, name, source_field, datatype):
+        """Initialise a WFS field.
+
+        Parameters
+        ----------
+        name : str
+            Name of this field in the return dataframe.
+        source_field : str
+            Name of this field in the source WFS service.
+        datatype : one of 'string', 'integer', 'float', 'date', 'datetime'
+                or 'boolean'
+            Datatype of the values of this field in the return dataframe.
+
+        """
+        super(WfsField, self).__init__(name, 'wfs', datatype)
+        self.__setitem__('sourcefield', source_field)
+
+
+class _WfsInjectedField(WfsField):
+    """Class for a field avaible in the WFS service, but not included in the
+    default dataframe output."""
+
+    def __init__(self, name, datatype):
+        """Initialise a WFS injected field.
+
+        This is a field not normally present in the dataframe, but useable as
+        a query and returnfield as it is available in the WFS service.
+
+        Parameters
+        ----------
+        name : str
+            Name of this field in the return dataframe.
+        datatype : one of 'string', 'integer', 'float', 'date', 'datetime'
+                or 'boolean'
+            Datatype of the values of this field in the return dataframe.
+
+        """
+        super(_WfsInjectedField, self).__init__(name, name, datatype)
+        self.__setitem__('wfs_injected', True)
+
+
+class XmlField(AbstractField):
+    """Class for a field available in the XML document."""
+
+    def __init__(self, name, source_xpath, datatype, definition='',
+                 notnull=False, xsd_type=None):
+        """Initialise an XML field.
+
+        Parameters
+        ----------
+        name : str
+            Name of this field in the return dataframe.
+        source_xpath : str
+            XPath expression of the values of this field in the source XML
+            document.
+        datatype : one of 'string', 'integer', 'float', 'date', 'datetime'
+                or 'boolean'
+            Datatype of the values of this field in the return dataframe.
+        definition : str, optional
+            Definition of this field.
+        notnull : bool, optional, defaults to False
+            True if this field is always present (mandatory), False otherwise.
+        xsd_type : pydov.types.abstract.XsdType, optional
+            XSD type associated with this field.
+
+        """
+        super(XmlField, self).__init__(name, 'xml', datatype)
+
+        self.__setitem__('sourcefield', source_xpath)
+        self.__setitem__('definition', definition)
+        self.__setitem__('notnull', notnull)
+
+        if xsd_type is not None:
+            self.__setitem__('xsd_schema', xsd_type.xsd_schema)
+            self.__setitem__('xsd_type', xsd_type.typename)
+
+
+class _CustomField(AbstractField):
+    """Class for a custom field, created explicitly in pydov."""
+
+    def __init__(self, name, datatype, definition='', notnull=False):
+        """Initialise a custom field.
+
+        Parameters
+        ----------
+        name : str
+            Name of this field in the return dataframe.
+        datatype : one of 'string', 'integer', 'float', 'date', 'datetime'
+                or 'boolean'
+            Datatype of the values of this field in the return dataframe.
+        definition : str, optional
+            Definition of this field.
+        notnull : bool, optional, defaults to False
+            True if this field is always present (mandatory), False otherwise.
+
+        """
+        super(_CustomField, self).__init__(name, 'custom', datatype)
+        self.__setitem__('definition', definition)
+        self.__setitem__('notnull', notnull)
diff --git a/pydov/types/grondwaterfilter.py b/pydov/types/grondwaterfilter.py
index f7852b1..e758efc 100644
--- a/pydov/types/grondwaterfilter.py
+++ b/pydov/types/grondwaterfilter.py
@@ -1,176 +1,122 @@
 # -*- coding: utf-8 -*-
 """Module containing the DOV data type for screens (Filter), including
 subtypes."""
-
+from pydov.types.fields import (
+    XmlField,
+    XsdType,
+    WfsField,
+)
 from .abstract import (
     AbstractDovType,
     AbstractDovSubType,
 )
 
+_filterDataCodes_xsd = 'https://www.dov.vlaanderen.be/xdov/schema/' \
+                       'latest/xsd/kern/gwmeetnet/FilterDataCodes.xsd'
+
 
 class Peilmeting(AbstractDovSubType):
 
-    _name = 'peilmeting'
-    _rootpath = './/filtermeting/peilmeting'
-
-    _fields = [{
-        'name': 'datum',
-        'source': 'xml',
-        'sourcefield': '/datum',
-        'definition': 'Datum van opmeten.',
-        'type': 'date',
-        'notnull': True
-    }, {
-        'name': 'tijdstip',
-        'source': 'xml',
-        'sourcefield': '/tijdstip',
-        'definition': 'Tijdstip van opmeten (optioneel).',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'peil_mtaw',
-        'source': 'xml',
-        'sourcefield': '/peil_mtaw',
-        'definition': 'Diepte van de peilmeting, uitgedrukt in mTAW.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'betrouwbaarheid',
-        'source': 'xml',
-        'sourcefield': '/betrouwbaarheid',
-        'definition': 'Lijst van betrouwbaarheden (goed, onbekend of'
-                      'twijfelachtig).',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'methode',
-        'source': 'xml',
-        'sourcefield': '/methode',
-        'xsd_type': 'PeilmetingMethodeEnumType',
-        'definition': 'Methode waarop de peilmeting uitgevoerd werd.',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'filterstatus',
-        'source': 'xml',
-        'sourcefield': '/filterstatus',
-        'xsd_type': 'FilterstatusEnumType',
-        'definition': 'Status van de filter tijdens de peilmeting (in rust - '
-                      'werking).',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'filtertoestand',
-        'source': 'xml',
-        'sourcefield': '/filtertoestand',
-        'xsd_type': 'FiltertoestandEnumType',
-        'definition':  "Filtertoestand bij de peilmeting. Standaardwaarde is "
-                       "'1' = Normaal.",
-        'type': 'integer',
-        'notnull': False
-    }]
+    rootpath = './/filtermeting/peilmeting'
+
+    fields = [
+        XmlField(name='datum',
+                 source_xpath='/datum',
+                 definition='Datum van opmeten.',
+                 datatype='date',
+                 notnull=True),
+        XmlField(name='tijdstip',
+                 source_xpath='/tijdstip',
+                 definition='Tijdstip van opmeten (optioneel).',
+                 datatype='string'),
+        XmlField(name='peil_mtaw',
+                 source_xpath='/peil_mtaw',
+                 definition='Diepte van de peilmeting, uitgedrukt in mTAW.',
+                 datatype='float'),
+        XmlField(name='betrouwbaarheid',
+                 source_xpath='/betrouwbaarheid',
+                 definition='Lijst van betrouwbaarheden (goed, onbekend of'
+                            'twijfelachtig).',
+                 datatype='string'),
+        XmlField(name='methode',
+                 source_xpath='/methode',
+                 definition='Methode waarop de peilmeting uitgevoerd werd.',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema=_filterDataCodes_xsd,
+                     typename='PeilmetingMethodeEnumType')),
+        XmlField(name='filterstatus',
+                 source_xpath='/filterstatus',
+                 definition='Status van de filter tijdens de peilmeting (in '
+                            'rust - werking).',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema=_filterDataCodes_xsd,
+                     typename='FilterstatusEnumType')),
+        XmlField(name='filtertoestand',
+                 source_xpath='/filtertoestand',
+                 definition="Filtertoestand bij de peilmeting. "
+                            "Standaardwaarde is '1' = Normaal.",
+                 datatype='integer',
+                 xsd_type=XsdType(
+                     xsd_schema=_filterDataCodes_xsd,
+                     typename='FiltertoestandEnumType'))
+    ]
 
 
 class GrondwaterFilter(AbstractDovType):
     """Class representing the DOV data type for Groundwater screens."""
 
-    _subtypes = [Peilmeting]
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'gwmeetnet/FilterDataCodes.xsd',
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/HydrogeologischeStratigrafieDataCodes.xsd'
+    subtypes = [Peilmeting]
+
+    fields = [
+        WfsField(name='pkey_filter', source_field='filterfiche',
+                 datatype='string'),
+        WfsField(name='pkey_grondwaterlocatie', source_field='putfiche',
+                 datatype='string'),
+        WfsField(name='gw_id', source_field='GW_ID', datatype='string'),
+        WfsField(name='filternummer', source_field='filternummer',
+                 datatype='string'),
+        WfsField(name='filtertype', source_field='filtertype',
+                 datatype='string'),
+        WfsField(name='x', source_field='X_mL72', datatype='float'),
+        WfsField(name='y', source_field='Y_mL72', datatype='float'),
+        WfsField(name='mv_mtaw', source_field='Z_mTAW', datatype='float'),
+        WfsField(name='gemeente', source_field='gemeente', datatype='string'),
+        XmlField(name='meetnet_code',
+                 source_xpath='/filter/meetnet',
+                 definition='Tot welk meetnet behoort deze filter.',
+                 datatype='integer',
+                 xsd_type=XsdType(
+                     xsd_schema=_filterDataCodes_xsd,
+                     typename='MeetnetEnumType')),
+        XmlField(name='aquifer_code',
+                 source_xpath='/filter/ligging/aquifer',
+                 definition='In welke watervoerende laag hangt de filter '
+                            '(code).',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'HydrogeologischeStratigrafieDataCodes.xsd',
+                     typename='AquiferEnumType')),
+        XmlField(name='grondwaterlichaam_code',
+                 source_xpath='/filter/ligging/grondwaterlichaam',
+                 definition='',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema=_filterDataCodes_xsd,
+                     typename='GrondwaterlichaamEnumType')),
+        XmlField(name='regime',
+                 source_xpath='/filter/ligging/regime',
+                 definition='',
+                 datatype='string'),
+        WfsField(name='diepte_onderkant_filter',
+                 source_field='onderkant_filter_m', datatype='float'),
+        WfsField(name='lengte_filter', source_field='lengte_filter_m',
+                 datatype='float')
     ]
 
-    _fields = [{
-        'name': 'pkey_filter',
-        'source': 'wfs',
-        'sourcefield': 'filterfiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_grondwaterlocatie',
-        'source': 'wfs',
-        'sourcefield': 'putfiche',
-        'type': 'string'
-    }, {
-        'name': 'gw_id',
-        'source': 'wfs',
-        'sourcefield': 'GW_ID',
-        'type': 'string'
-    }, {
-        'name': 'filternummer',
-        'source': 'wfs',
-        'sourcefield': 'filternummer',
-        'type': 'string'
-    }, {
-        'name': 'filtertype',
-        'source': 'wfs',
-        'sourcefield': 'filtertype',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }, {
-        'name': 'mv_mtaw',
-        'source': 'wfs',
-        'sourcefield': 'Z_mTAW',
-        'type': 'float'
-    }, {
-        'name': 'gemeente',
-        'source': 'wfs',
-        'sourcefield': 'gemeente',
-        'type': 'string'
-    }, {
-        'name': 'meetnet_code',
-        'source': 'xml',
-        'sourcefield': '/filter/meetnet',
-        'xsd_type': 'MeetnetEnumType',
-        'definition': 'Tot welk meetnet behoort deze filter.',
-        'type': 'integer',
-        'notnull': False
-    }, {
-        'name': 'aquifer_code',
-        'source': 'xml',
-        'sourcefield': '/filter/ligging/aquifer',
-        'xsd_type': 'AquiferEnumType',
-        'definition': 'In welke watervoerende laag hangt de filter (code).',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'grondwaterlichaam_code',
-        'source': 'xml',
-        'sourcefield': '/filter/ligging/grondwaterlichaam',
-        'xsd_type': 'GrondwaterlichaamEnumType',
-        'definition': '',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'regime',
-        'source': 'xml',
-        'sourcefield': '/filter/ligging/regime',
-        'definition': '',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'diepte_onderkant_filter',
-        'source': 'wfs',
-        'sourcefield': 'onderkant_filter_m',
-        'type': 'float'
-    }, {
-        'name': 'lengte_filter',
-        'source': 'wfs',
-        'sourcefield': 'lengte_filter_m',
-        'type': 'float'
-    }]
-
     def __init__(self, pkey):
         """Initialisation.
 
@@ -178,7 +124,7 @@ class GrondwaterFilter(AbstractDovType):
         ----------
         pkey : str
             Permanent key of the Filter (screen), being a URI of the form
-            `https://www.dov.vlaanderen.be/data/boring/<id>`.
+            `https://www.dov.vlaanderen.be/data/filter/<id>`.
 
         """
         super(GrondwaterFilter, self).__init__('filter', pkey)
@@ -201,7 +147,7 @@ class GrondwaterFilter(AbstractDovType):
             element.
 
         """
-        gwfilter = GrondwaterFilter(
+        gwfilter = cls(
             feature.findtext('./{{{}}}filterfiche'.format(namespace)))
 
         for field in cls.get_fields(source=('wfs',)).values():
diff --git a/pydov/types/interpretaties.py b/pydov/types/interpretaties.py
index 746f8bc..59c0e92 100644
--- a/pydov/types/interpretaties.py
+++ b/pydov/types/interpretaties.py
@@ -7,11 +7,39 @@ from pydov.types.abstract import (
     AbstractDovType,
     AbstractDovSubType,
 )
+from pydov.types.fields import (
+    WfsField,
+    _CustomField,
+    XmlField,
+    XsdType,
+)
 
 
 class AbstractCommonInterpretatie(AbstractDovType):
     """Abstract base class for interpretations that can be linked to
     boreholes or cone penetration tests."""
+
+    fields = [
+        WfsField(name='pkey_interpretatie',
+                 source_field='Interpretatiefiche', datatype='string'),
+        _CustomField(name='pkey_boring',
+                     definition='URL die verwijst naar de gegevens van de '
+                                'boring waaraan deze informele stratigrafie '
+                                'gekoppeld is (indien gekoppeld aan een '
+                                'boring).',
+                     datatype='string'),
+        _CustomField(name='pkey_sondering',
+                     definition='URL die verwijst naar de gegevens van de '
+                                'sondering waaraan deze informele '
+                                'stratigrafie gekoppeld is (indien gekoppeld '
+                                'aan een sondering).',
+                     datatype='string'),
+        WfsField(name='betrouwbaarheid_interpretatie',
+                 source_field='Betrouwbaarheid', datatype='string'),
+        WfsField(name='x', source_field='X_mL72', datatype='float'),
+        WfsField(name='y', source_field='Y_mL72', datatype='float')
+    ]
+
     def __init__(self, pkey):
         """Initialisation.
 
@@ -91,6 +119,18 @@ class AbstractCommonInterpretatie(AbstractDovType):
 class AbstractBoringInterpretatie(AbstractDovType):
     """Abstract base class for interpretations that are linked to boreholes
     only."""
+
+    fields = [
+        WfsField(name='pkey_interpretatie',
+                 source_field='Interpretatiefiche', datatype='string'),
+        WfsField(name='pkey_boring', source_field='Proeffiche',
+                 datatype='string'),
+        WfsField(name='betrouwbaarheid_interpretatie',
+                 source_field='Betrouwbaarheid', datatype='string'),
+        WfsField(name='x', source_field='X_mL72', datatype='float'),
+        WfsField(name='y', source_field='Y_mL72', datatype='float')
+    ]
+
     def __init__(self, pkey):
         """Initialisation.
 
@@ -139,596 +179,336 @@ class AbstractBoringInterpretatie(AbstractDovType):
 
 class InformeleStratigrafieLaag(AbstractDovSubType):
 
-    _name = 'informele_stratigrafie_laag'
-    _rootpath = './/informelestratigrafie/laag'
-
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Diepte van de bovenkant van de laag informele '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag informele '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'beschrijving',
-        'source': 'xml',
-        'sourcefield': '/beschrijving',
-        'definition': 'Benoeming van de eenheid van de laag informele '
-                      'stratigrafie in vrije tekst (onbeperkt in lengte).',
-        'type': 'string',
-        'notnull': False
-    }]
+    rootpath = './/informelestratigrafie/laag'
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag informele '
+                            'stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag informele '
+                            'stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='beschrijving',
+                 source_xpath='/beschrijving',
+                 definition='Benoeming van de eenheid van de laag informele '
+                            'stratigrafie in vrije tekst (onbeperkt in '
+                            'lengte).',
+                 datatype='string')
+    ]
 
 
 class InformeleStratigrafie(AbstractCommonInterpretatie):
     """Class representing the DOV data type for 'informele stratigrafie'
     interpretations."""
 
-    _subtypes = [InformeleStratigrafieLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'custom',
-        'type': 'string',
-        'definition': 'URL die verwijst naar de gegevens van de boring '
-                      'waaraan deze informele stratigrafie gekoppeld is ('
-                      'indien gekoppeld aan een boring).',
-        'notnull': False
-    }, {
-        'name': 'pkey_sondering',
-        'source': 'custom',
-        'type': 'string',
-        'definition': 'URL die verwijst naar de gegevens van de sondering '
-                      'waaraan deze informele stratigrafie gekoppeld is ('
-                      'indien gekoppeld aan een sondering).',
-        'notnull': False
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [InformeleStratigrafieLaag]
 
 
 class FormeleStratigrafieLaag(AbstractDovSubType):
 
-    _name = 'formele_stratigrafie_laag'
-    _rootpath = './/formelestratigrafie/laag'
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/InterpretatieDataCodes.xsd',
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/FormeleStratigrafieDataCodes.xsd'
-    ]
-
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Diepte van de bovenkant van de laag Formele '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag Formele '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'lid1',
-        'source': 'xml',
-        'sourcefield': '/lid1',
-        'xsd_type': 'FormeleStratigrafieLedenEnumType',
-        'definition': 'eerste eenheid van de laag formele stratigrafie',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'relatie_lid1_lid2',
-        'source': 'xml',
-        'sourcefield': '/relatie_lid1_lid2',
-        'xsd_type': 'RelatieLedenEnumType',
-        'definition': 'verbinding/relatie tussen lid1 en lid2 van de laag '
-                      'formele stratigrafie',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'lid2',
-        'source': 'xml',
-        'sourcefield': '/lid2',
-        'xsd_type': 'FormeleStratigrafieLedenEnumType',
-        'definition': 'tweede eenheid van de laag formele stratigrafie. '
+    rootpath = './/formelestratigrafie/laag'
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag Formele '
+                            'stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag Formele '
+                            'stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='lid1',
+                 source_xpath='/lid1',
+                 definition='eerste eenheid van de laag formele stratigrafie',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'FormeleStratigrafieDataCodes.xsd',
+                     typename='FormeleStratigrafieLedenEnumType')),
+        XmlField(name='relatie_lid1_lid2',
+                 source_xpath='/relatie_lid1_lid2',
+                 definition='verbinding/relatie tussen lid1 en lid2 van de '
+                            'laag formele stratigrafie',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'InterpretatieDataCodes.xsd',
+                     typename='RelatieLedenEnumType')),
+        XmlField(name='lid2',
+                 source_xpath='/lid2',
+                 definition='tweede eenheid van de laag formele stratigrafie. '
                       'Indien niet ingevuld wordt default de waarde van lid1 '
                       'ingevuld',
-        'type': 'string',
-        'notnull': False
-    }]
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'FormeleStratigrafieDataCodes.xsd',
+                     typename='FormeleStratigrafieLedenEnumType'))
+    ]
 
 
 class FormeleStratigrafie(AbstractCommonInterpretatie):
     """Class representing the DOV data type for 'Formele stratigrafie'
     interpretations."""
 
-    _subtypes = [FormeleStratigrafieLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'custom',
-        'type': 'string',
-        'definition': 'URL die verwijst naar de gegevens van de boring '
-                      'waaraan deze formele stratigrafie gekoppeld is ('
-                      'indien gekoppeld aan een boring).',
-        'notnull': False
-    }, {
-        'name': 'pkey_sondering',
-        'source': 'custom',
-        'type': 'string',
-        'definition': 'URL die verwijst naar de gegevens van de sondering '
-                      'waaraan deze formele stratigrafie gekoppeld is ('
-                      'indien gekoppeld aan een sondering).',
-        'notnull': False
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [FormeleStratigrafieLaag]
 
 
 class HydrogeologischeStratigrafieLaag(AbstractDovSubType):
 
-    _name = 'hydrogeologische_stratigrafie_laag'
-    _rootpath = './/hydrogeologischeinterpretatie/laag'
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/HydrogeologischeStratigrafieDataCodes.xsd'
+    rootpath = './/hydrogeologischeinterpretatie/laag'
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag '
+                            'hydrogeologische stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag '
+                            'hydrogeologische stratigrafie in meter.',
+                 datatype='float'),
+        XmlField(name='aquifer',
+                 source_xpath='/aquifer',
+                 definition='code van de watervoerende laag waarin de laag '
+                            'Hydrogeologische stratigrafie zich bevindt.',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'HydrogeologischeStratigrafieDataCodes.xsd',
+                     typename='AquiferEnumType'
+                 ))
     ]
 
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Diepte van de bovenkant van de laag hydrogeologische '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag hydrogeologische '
-                      'stratigrafie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'aquifer',
-        'source': 'xml',
-        'sourcefield': '/aquifer',
-        'xsd_type': 'AquiferEnumType',
-        'definition': 'code van de watervoerende laag waarin de laag '
-                      'Hydrogeologische stratigrafie zich bevindt.',
-        'type': 'string',
-        'notnull': False
-    }]
-
 
 class HydrogeologischeStratigrafie(AbstractBoringInterpretatie):
     """Class representing the DOV data type for 'hydrogeologische
     stratigrafie' interpretations."""
 
-    _subtypes = [HydrogeologischeStratigrafieLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'type': 'string',
-        'sourcefield': 'Proeffiche'
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    },  {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [HydrogeologischeStratigrafieLaag]
 
 
 class LithologischeBeschrijvingLaag(AbstractDovSubType):
 
-    _name = 'lithologische_beschrijving_laag'
-    _rootpath = './/lithologischebeschrijving/laag'
-
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Diepte van de bovenkant van de laag lithologische '
-                      'beschrijving in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag lithologische '
-                      'beschrijving in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'beschrijving',
-        'source': 'xml',
-        'sourcefield': '/beschrijving',
-        'definition': 'Lithologische beschrijving van de laag in vrije tekst '
-                      '(onbeperkt in lengte)',
-        'type': 'string',
-        'notnull': False
-    }]
+    rootpath = './/lithologischebeschrijving/laag'
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag '
+                            'lithologische beschrijving in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag '
+                            'lithologische beschrijving in meter.',
+                 datatype='float'),
+        XmlField(name='beschrijving',
+                 source_xpath='/beschrijving',
+                 definition='Lithologische beschrijving van de laag in vrije '
+                            'tekst (onbeperkt in lengte)',
+                 datatype='string')
+    ]
 
 
 class LithologischeBeschrijvingen(AbstractBoringInterpretatie):
     """Class representing the DOV data type for 'lithologische
     beschrijvingen' interpretations."""
 
-    _subtypes = [LithologischeBeschrijvingLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'type': 'string',
-        'sourcefield': 'Proeffiche',
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    },  {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [LithologischeBeschrijvingLaag]
 
 
 class GecodeerdeLithologieLaag(AbstractDovSubType):
 
-    _name = 'gecodeerde_lithologie_laag'
-    _rootpath = './/gecodeerdelithologie/laag'
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/GecodeerdeLithologieDataCodes.xsd'
+    rootpath = './/gecodeerdelithologie/laag'
+
+    __gecodeerdHoofdnaamCodesEnumType = XsdType(
+        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
+                   'kern/interpretatie/GecodeerdeLithologieDataCodes.xsd',
+        typename='GecodeerdHoofdnaamCodesEnumType'
+    )
+
+    __gecodeerdBijmengingHoeveelheidEnumType = XsdType(
+        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
+                   'kern/interpretatie/GecodeerdeLithologieDataCodes.xsd',
+        typename='GecodeerdBijmengingHoeveelheidEnumType'
+    )
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag '
+                            'gecodeerde lithologie in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag '
+                            'gecodeerde lithologie in meter.',
+                 datatype='float'),
+        XmlField(name='hoofdnaam1_grondsoort',
+                 source_xpath='/hoofdnaam[1]/grondsoort',
+                 definition='Primaire grondsoort (als code) van de laag '
+                            'gecodeerde lithologie',
+                 datatype='string',
+                 xsd_type=__gecodeerdHoofdnaamCodesEnumType),
+        XmlField(name='hoofdnaam2_grondsoort',
+                 source_xpath='/hoofdnaam[2]/grondsoort',
+                 definition='Secundaire grondsoort (als code) van de laag '
+                            'gecodeerde lithologie',
+                 datatype='string',
+                 xsd_type=__gecodeerdHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging1_plaatselijk',
+                 source_xpath='/bijmenging[1]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging1_hoeveelheid',
+                 source_xpath='/bijmenging[1]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gecodeerdBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging1_grondsoort',
+                 source_xpath='/bijmenging[1]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'gecodeerde lithologie of geotechnische codering',
+                 datatype='string',
+                 xsd_type=__gecodeerdHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging2_plaatselijk',
+                 source_xpath='/bijmenging[2]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging2_hoeveelheid',
+                 source_xpath='/bijmenging[2]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gecodeerdBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging2_grondsoort',
+                 source_xpath='/bijmenging[2]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'gecodeerde lithologie of geotechnische codering',
+                 datatype='string',
+                 xsd_type=__gecodeerdHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging3_plaatselijk',
+                 source_xpath='/bijmenging[3]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging3_hoeveelheid',
+                 source_xpath='/bijmenging[3]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gecodeerdBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging3_grondsoort',
+                 source_xpath='/bijmenging[3]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'gecodeerde lithologie of geotechnische codering',
+                 datatype='string',
+                 xsd_type=__gecodeerdHoofdnaamCodesEnumType)
     ]
 
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'Diepte van de bovenkant van de laag gecodeerde'
-                      ' lithologie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag gecodeerde'
-                      ' lithologie in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'hoofdnaam1_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/hoofdnaam[1]/grondsoort',
-        'definition': 'Primaire grondsoort (als code) van de laag '
-                      'gecodeerde lithologie',
-        'xsd_type': 'GecodeerdHoofdnaamCodesEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'hoofdnaam2_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/hoofdnaam[2]/grondsoort',
-        'definition': 'Secundaire grondsoort (als code) van de laag '
-                      'gecodeerde lithologie',
-        'xsd_type': 'GecodeerdHoofdnaamCodesEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/hoeveelheid',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'xsd_type': 'GecodeerdBijmengingHoeveelheidEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/grondsoort',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'gecodeerde lithologie of geotechnische '
-                      'codering',
-        'xsd_type': 'GecodeerdHoofdnaamCodesEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/hoeveelheid',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'xsd_type': 'GecodeerdBijmengingHoeveelheidEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/grondsoort',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'gecodeerde lithologie of geotechnische '
-                      'codering',
-        'xsd_type': 'GecodeerdHoofdnaamCodesEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/hoeveelheid',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'xsd_type': 'GecodeerdBijmengingHoeveelheidEnumType',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/grondsoort',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'gecodeerde lithologie of geotechnische '
-                      'codering',
-        'xsd_type': 'GecodeerdHoofdnaamCodesEnumType',
-        'type': 'string',
-        'notnull': False
-    }]
-
 
 class GecodeerdeLithologie(AbstractBoringInterpretatie):
     """Class representing the DOV data type for 'gecodeerde
     lithologie' interpretations."""
 
-    _subtypes = [GecodeerdeLithologieLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'type': 'string',
-        'sourcefield': 'Proeffiche',
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    },  {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [GecodeerdeLithologieLaag]
 
 
 class GeotechnischeCoderingLaag(AbstractDovSubType):
 
-    _name = 'geotechnische_codering_laag'
-    _rootpath = './/geotechnischecodering/laag'
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/GeotechnischeCoderingDataCodes.xsd'
+    rootpath = './/geotechnischecodering/laag'
+
+    __geotechnischeCoderingHoofdnaamCodesEnumType = XsdType(
+        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
+                   'kern/interpretatie/GeotechnischeCoderingDataCodes.xsd',
+        typename='GeotechnischeCoderingHoofdnaamCodesEnumType'
+    )
+
+    __gtCoderingBijmengingHoeveelheidEnumType = XsdType(
+        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
+                   'kern/interpretatie/GeotechnischeCoderingDataCodes.xsd',
+        typename='GeotechnischeCoderingBijmengingHoeveelheidEnumType'
+    )
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='Diepte van de bovenkant van de laag '
+                            'geotechnische codering in meter.',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='Diepte van de onderkant van de laag '
+                            'geotechnische codering in meter.',
+                 datatype='float'),
+        XmlField(name='hoofdnaam1_grondsoort',
+                 source_xpath='/hoofdnaam[1]/grondsoort',
+                 definition='hoofdnaam (als code) van de laag geotechnische '
+                            'codering',
+                 datatype='string',
+                 xsd_type=__geotechnischeCoderingHoofdnaamCodesEnumType),
+        XmlField(name='hoofdnaam2_grondsoort',
+                 source_xpath='/hoofdnaam[2]/grondsoort',
+                 definition='Secundaire grondsoort (als code) van de laag '
+                            'geotechnische codering',
+                 datatype='string',
+                 xsd_type=__geotechnischeCoderingHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging1_plaatselijk',
+                 source_xpath='/bijmenging[1]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging1_hoeveelheid',
+                 source_xpath='/bijmenging[1]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gtCoderingBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging1_grondsoort',
+                 source_xpath='/bijmenging[1]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'geotechnische codering',
+                 datatype='string',
+                 xsd_type=__geotechnischeCoderingHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging2_plaatselijk',
+                 source_xpath='/bijmenging[2]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging2_hoeveelheid',
+                 source_xpath='/bijmenging[2]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gtCoderingBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging2_grondsoort',
+                 source_xpath='/bijmenging[2]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'geotechnische codering',
+                 datatype='string',
+                 xsd_type=__geotechnischeCoderingHoofdnaamCodesEnumType),
+        XmlField(name='bijmenging3_plaatselijk',
+                 source_xpath='/bijmenging[3]/plaatselijk',
+                 definition='plaatselijk of niet-plaatselijk',
+                 datatype='boolean'),
+        XmlField(name='bijmenging3_hoeveelheid',
+                 source_xpath='/bijmenging[3]/hoeveelheid',
+                 definition='aanduiding van de hoeveelheid bijmenging',
+                 datatype='string',
+                 xsd_type=__gtCoderingBijmengingHoeveelheidEnumType),
+        XmlField(name='bijmenging3_grondsoort',
+                 source_xpath='/bijmenging[3]/grondsoort',
+                 definition='type grondsoort (als code) van de laag '
+                            'geotechnische codering',
+                 datatype='string',
+                 xsd_type=__geotechnischeCoderingHoofdnaamCodesEnumType)
     ]
 
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'diepte van de bovenkant van de laag geotechnische '
-                      'codering in meter',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'Diepte van de onderkant van de laag geotechnische'
-                      ' codering in meter.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'hoofdnaam1_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/hoofdnaam[1]/grondsoort',
-        'xsd_type': 'GeotechnischeCoderingHoofdnaamCodesEnumType',
-        'definition': 'hoofdnaam (als code) van de laag '
-                      'geotechnische codering',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'hoofdnaam2_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/hoofdnaam[2]/grondsoort',
-        'xsd_type': 'GeotechnischeCoderingHoofdnaamCodesEnumType',
-        'definition': 'Secundaire grondsoort (als code) van de laag '
-                      'geotechnische codering',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/hoeveelheid',
-        'xsd_type': 'GeotechnischeCoderingBijmengingHoeveelheidEnumType',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging1_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[1]/grondsoort',
-        'xsd_type': 'GeotechnischeCoderingHoofdnaamCodesEnumType',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'geotechnische codering',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/hoeveelheid',
-        'xsd_type': 'GeotechnischeCoderingBijmengingHoeveelheidEnumType',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging2_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[2]/grondsoort',
-        'xsd_type': 'GeotechnischeCoderingHoofdnaamCodesEnumType',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'geotechnische codering',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_plaatselijk',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/plaatselijk',
-        'definition': 'plaatselijk of niet-plaatselijk',
-        'type': 'boolean',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_hoeveelheid',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/hoeveelheid',
-        'xsd_type': 'GeotechnischeCoderingBijmengingHoeveelheidEnumType',
-        'definition': 'aanduiding van de hoeveelheid bijmenging',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'bijmenging3_grondsoort',
-        'source': 'xml',
-        'sourcefield': '/bijmenging[3]/grondsoort',
-        'xsd_type': 'GeotechnischeCoderingHoofdnaamCodesEnumType',
-        'definition': 'type grondsoort (als code) van de laag '
-                      'geotechnische codering',
-        'type': 'string',
-        'notnull': False
-    }]
-
 
 class GeotechnischeCodering(AbstractBoringInterpretatie):
     """Class representing the DOV data type for 'geotechnische
@@ -739,92 +519,55 @@ class GeotechnischeCodering(AbstractBoringInterpretatie):
     rekening houdend met informatie uit de lithologie,
     laboproeven en bijhorende sondering(en)."""
 
-    _subtypes = [GeotechnischeCoderingLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'type': 'string',
-        'sourcefield': 'Proeffiche',
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    },  {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [GeotechnischeCoderingLaag]
 
 
 class QuartairStratigrafieLaag(AbstractDovSubType):
 
-    _name = 'quartairestratigrafie_laag'
-    _rootpath = './/quartairstratigrafie/laag'
-
-    _xsd_schemas = [
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/InterpretatieDataCodes.xsd',
-        'https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/kern/'
-        'interpretatie/QuartairStratigrafieDataCodes.xsd'
-    ]
-
-    _fields = [{
-        'name': 'diepte_laag_van',
-        'source': 'xml',
-        'sourcefield': '/van',
-        'definition': 'diepte van de bovenkant van de laag '
-                      'quartairstratigrafie in meter',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'diepte_laag_tot',
-        'source': 'xml',
-        'sourcefield': '/tot',
-        'definition': 'diepte van de onderkant van de laag '
-                      'quartairstratigrafie in meter',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'lid1',
-        'source': 'xml',
-        'sourcefield': '/lid1',
-        'xsd_type': 'QuartairStratigrafieLedenEnumType',
-        'definition': 'eerste eenheid van de laag quartairstratigrafie',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'relatie_lid1_lid2',
-        'source': 'xml',
-        'sourcefield': '/relatie_lid1_lid2',
-        'xsd_type': 'RelatieLedenEnumType',
-        'definition': 'verbinding of relatie tussen lid1 en lid2 van de '
-                      'laag quartairstratigrafie',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'lid2',
-        'source': 'xml',
-        'sourcefield': '/lid2',
-        'xsd_type': 'QuartairStratigrafieLedenEnumType',
-        'definition': 'tweede eenheid van de laag quartairstratigrafie. '
+    rootpath = './/quartairstratigrafie/laag'
+
+    fields = [
+        XmlField(name='diepte_laag_van',
+                 source_xpath='/van',
+                 definition='diepte van de bovenkant van de laag '
+                            'quartairstratigrafie in meter',
+                 datatype='float'),
+        XmlField(name='diepte_laag_tot',
+                 source_xpath='/tot',
+                 definition='diepte van de onderkant van de laag '
+                            'quartairstratigrafie in meter',
+                 datatype='float'),
+        XmlField(name='lid1',
+                 source_xpath='/lid1',
+                 definition='eerste eenheid van de laag quartairstratigrafie',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'QuartairStratigrafieDataCodes.xsd',
+                     typename='QuartairStratigrafieLedenEnumType')),
+        XmlField(name='relatie_lid1_lid2',
+                 source_xpath='/relatie_lid1_lid2',
+                 definition='verbinding of relatie tussen lid1 en lid2 van de '
+                            'laag quartairstratigrafie',
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'InterpretatieDataCodes.xsd',
+                     typename='RelatieLedenEnumType')),
+        XmlField(name='lid2',
+                 source_xpath='/lid2',
+                 definition='tweede eenheid van de laag quartairstratigrafie. '
                       'Indien niet ingevuld wordt default dezelfde waarde '
                       'als voor Lid1 ingevuld',
-        'type': 'string',
-        'notnull': False
-    }]
+                 datatype='string',
+                 xsd_type=XsdType(
+                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
+                                'latest/xsd/kern/interpretatie/'
+                                'QuartairStratigrafieDataCodes.xsd',
+                     typename='QuartairStratigrafieLedenEnumType'))
+    ]
 
 
 class QuartairStratigrafie(AbstractBoringInterpretatie):
@@ -840,31 +583,4 @@ class QuartairStratigrafie(AbstractBoringInterpretatie):
     ipv lithostratigrafie
     """
 
-    _subtypes = [QuartairStratigrafieLaag]
-
-    _fields = [{
-        'name': 'pkey_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Interpretatiefiche',
-        'type': 'string'
-    }, {
-        'name': 'pkey_boring',
-        'source': 'wfs',
-        'sourcefield': 'Proeffiche',
-        'type': 'string',
-    }, {
-        'name': 'betrouwbaarheid_interpretatie',
-        'source': 'wfs',
-        'sourcefield': 'Betrouwbaarheid',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }]
+    subtypes = [QuartairStratigrafieLaag]
diff --git a/pydov/types/sondering.py b/pydov/types/sondering.py
index 2c235ff..b18b891 100644
--- a/pydov/types/sondering.py
+++ b/pydov/types/sondering.py
@@ -5,142 +5,89 @@ from pydov.types.abstract import (
     AbstractDovType,
     AbstractDovSubType,
 )
+from pydov.types.fields import (
+    XmlField,
+    WfsField,
+)
 
 
 class Meetdata(AbstractDovSubType):
 
-    _name = 'penetratietest'
-    _rootpath = './/sondering/sondeonderzoek/penetratietest/meetdata'
-
-    _fields = [{
-        'name': 'z',
-        'source': 'xml',
-        'sourcefield': '/sondeerdiepte',
-        'definition': 'Diepte waarop sondeerparameters geregistreerd werden, '
-                      'uitgedrukt in meter ten opzicht van het aanvangspeil.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'qc',
-        'source': 'xml',
-        'sourcefield': '/qc',
-        'definition': 'Opgemeten waarde van de conusweerstand, uitgedrukt in '
-                      'MPa.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'Qt',
-        'source': 'xml',
-        'sourcefield': '/Qt',
-        'definition': 'Opgemeten waarde van de totale weerstand, uitgedrukt '
-                      'in kN.',
-        'type': 'string',
-        'notnull': False
-    }, {
-        'name': 'fs',
-        'source': 'xml',
-        'sourcefield': '/fs',
-        'definition': 'Opgemeten waarde van de plaatelijke kleefweerstand, '
-                      'uitgedrukt in kPa.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'u',
-        'source': 'xml',
-        'sourcefield': '/u',
-        'definition': 'Opgemeten waarde van de porienwaterspanning, '
-                      'uitgedrukt in kPa.',
-        'type': 'float',
-        'notnull': False
-    }, {
-        'name': 'i',
-        'source': 'xml',
-        'sourcefield': '/i',
-        'definition': 'Opgemeten waarde van de inclinatie, uitgedrukt in '
-                      'graden.',
-        'type': 'float',
-        'notnull': False
-    }]
+    rootpath = './/sondering/sondeonderzoek/penetratietest/meetdata'
+
+    fields = [
+        XmlField(name='z',
+                 source_xpath='/sondeerdiepte',
+                 definition='Diepte waarop sondeerparameters geregistreerd '
+                            'werden, uitgedrukt in meter ten opzicht van het '
+                            'aanvangspeil.',
+                 datatype='float'),
+        XmlField(name='qc',
+                 source_xpath='/qc',
+                 definition='Opgemeten waarde van de conusweerstand, '
+                            'uitgedrukt in MPa.',
+                 datatype='float'),
+        XmlField(name='Qt',
+                 source_xpath='/Qt',
+                 definition='Opgemeten waarde van de totale weerstand, '
+                            'uitgedrukt in kN.',
+                 datatype='float'),
+        XmlField(name='fs',
+                 source_xpath='/fs',
+                 definition='Opgemeten waarde van de plaatelijke '
+                            'kleefweerstand, uitgedrukt in kPa.',
+                 datatype='float'),
+        XmlField(name='u',
+                 source_xpath='/u',
+                 definition='Opgemeten waarde van de porienwaterspanning, '
+                            'uitgedrukt in kPa.',
+                 datatype='float'),
+        XmlField(name='i',
+                 source_xpath='/i',
+                 definition='Opgemeten waarde van de inclinatie, uitgedrukt '
+                            'in graden.',
+                 datatype='float')
+    ]
 
 
 class Sondering(AbstractDovType):
     """Class representing the DOV data type for CPT measurements."""
 
-    _subtypes = [Meetdata]
-
-    _fields = [{
-        'name': 'pkey_sondering',
-        'source': 'wfs',
-        'sourcefield': 'fiche',
-        'type': 'string'
-    }, {
-        'name': 'sondeernummer',
-        'source': 'wfs',
-        'sourcefield': 'sondeernummer',
-        'type': 'string'
-    }, {
-        'name': 'x',
-        'source': 'wfs',
-        'sourcefield': 'X_mL72',
-        'type': 'float'
-    }, {
-        'name': 'y',
-        'source': 'wfs',
-        'sourcefield': 'Y_mL72',
-        'type': 'float'
-    }, {
-        'name': 'start_sondering_mtaw',
-        'source': 'wfs',
-        'sourcefield': 'Z_mTAW',
-        'type': 'float'
-    }, {
-        'name': 'diepte_sondering_van',
-        'source': 'wfs',
-        'sourcefield': 'diepte_van_m',
-        'type': 'float'
-    }, {
-        'name': 'diepte_sondering_tot',
-        'source': 'wfs',
-        'sourcefield': 'diepte_tot_m',
-        'type': 'float'
-    }, {
-        'name': 'datum_aanvang',
-        'source': 'wfs',
-        'sourcefield': 'datum_aanvang',
-        'type': 'date'
-    }, {
-        'name': 'uitvoerder',
-        'source': 'wfs',
-        'sourcefield': 'uitvoerder',
-        'type': 'string'
-    }, {
-        'name': 'sondeermethode',
-        'source': 'wfs',
-        'sourcefield': 'sondeermethode',
-        'type': 'string'
-    }, {
-        'name': 'apparaat',
-        'source': 'wfs',
-        'sourcefield': 'apparaat_type',
-        'type': 'string'
-    }, {
-        'name': 'datum_gw_meting',
-        'source': 'xml',
-        'sourcefield': '/sondering/visueelonderzoek/'
-                       'datumtijd_waarneming_grondwaterstand',
-        'definition': 'Datum en tijdstip van waarneming van de '
-                      'grondwaterstand.',
-        'type': 'datetime',
-        'notnull': False
-    }, {
-        'name': 'diepte_gw_m',
-        'source': 'xml',
-        'sourcefield': '/sondering/visueelonderzoek/grondwaterstand',
-        'definition': 'Diepte water in meter ten opzicht van het '
-                      'aanvangspeil.',
-        'type': 'float',
-        'notnull': False
-    }]
+    subtypes = [Meetdata]
+
+    fields = [
+        WfsField(name='pkey_sondering', source_field='fiche',
+                 datatype='string'),
+        WfsField(name='sondeernummer', source_field='sondeernummer',
+                 datatype='string'),
+        WfsField(name='x', source_field='X_mL72', datatype='float'),
+        WfsField(name='y', source_field='Y_mL72', datatype='float'),
+        WfsField(name='start_sondering_mtaw', source_field='Z_mTAW',
+                 datatype='float'),
+        WfsField(name='diepte_sondering_van', source_field='diepte_van_m',
+                 datatype='float'),
+        WfsField(name='diepte_sondering_tot', source_field='diepte_tot_m',
+                 datatype='float'),
+        WfsField(name='datum_aanvang', source_field='datum_aanvang',
+                 datatype='date'),
+        WfsField(name='uitvoerder', source_field='uitvoerder',
+                 datatype='string'),
+        WfsField(name='sondeermethode', source_field='sondeermethode',
+                 datatype='string'),
+        WfsField(name='apparaat', source_field='apparaat_type',
+                 datatype='string'),
+        XmlField(name='datum_gw_meting',
+                 source_xpath='/sondering/visueelonderzoek/'
+                              'datumtijd_waarneming_grondwaterstand',
+                 definition='Datum en tijdstip van waarneming van de '
+                            'grondwaterstand.',
+                 datatype='datetime'),
+        XmlField(name='diepte_gw_m',
+                 source_xpath='/sondering/visueelonderzoek/grondwaterstand',
+                 definition='Diepte water in meter ten opzicht van het '
+                            'aanvangspeil.',
+                 datatype='float')
+    ]
 
     def __init__(self, pkey):
         """Initialisation.
@@ -172,7 +119,7 @@ class Sondering(AbstractDovType):
             element.
 
         """
-        s = Sondering(feature.findtext('./{{{}}}fiche'.format(namespace)))
+        s = cls(feature.findtext('./{{{}}}fiche'.format(namespace)))
 
         for field in cls.get_fields(source=('wfs',)).values():
             s.data[field['name']] = cls._parse(


Test patch:
diff --git a/tests/abstract.py b/tests/abstract.py
index b2cd249..073a920 100644
--- a/tests/abstract.py
+++ b/tests/abstract.py
@@ -18,6 +18,7 @@ from pandas.api.types import (
 
 from owslib.fes import PropertyIsEqualTo
 from owslib.etree import etree
+from pydov.types.abstract import AbstractField
 from pydov.util.errors import InvalidFieldError
 from pydov.util.location import (
     Within,
@@ -191,6 +192,13 @@ class AbstractTestSearch(object):
         """
         raise NotImplementedError
 
+    def test_pluggable_type(self):
+        """Test whether the search object can be initialised by explicitly
+        giving the objecttype.
+        """
+        datatype = self.get_type()
+        self.get_search_object().__class__(objecttype=datatype)
+
     def test_get_fields(self, mp_wfs, mp_remote_describefeaturetype,
                         mp_remote_md, mp_remote_fc, mp_remote_xsd):
         """Test the get_fields method.
@@ -779,7 +787,7 @@ class AbstractTestTypes(object):
             assert type(f) in (str, unicode)
 
             field = fields[f]
-            assert type(field) is dict
+            assert isinstance(field, AbstractField)
 
             assert 'name' in field
             assert type(field['name']) in (str, unicode)
@@ -815,7 +823,7 @@ class AbstractTestTypes(object):
                 if 'xsd_type' in field:
                     assert sorted(field.keys()) == [
                         'definition', 'name', 'notnull', 'source',
-                        'sourcefield', 'type', 'xsd_type']
+                        'sourcefield', 'type', 'xsd_schema', 'xsd_type']
                 else:
                     assert sorted(field.keys()) == [
                         'definition', 'name', 'notnull', 'source',
diff --git a/tests/test_types.py b/tests/test_types.py
index 979a2fb..8f4c969 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -3,18 +3,29 @@
 import pytest
 
 from pydov.types.boring import Boring
+from pydov.types.fields import XmlField
 from pydov.types.grondwaterfilter import GrondwaterFilter
-from pydov.types.interpretaties import InformeleStratigrafie
-from pydov.search.interpretaties import HydrogeologischeStratigrafie
-from pydov.search.interpretaties import GecodeerdeLithologie
-from pydov.search.interpretaties import LithologischeBeschrijvingen
+from pydov.types.interpretaties import (
+    GecodeerdeLithologie,
+    HydrogeologischeStratigrafie,
+    InformeleStratigrafie,
+    LithologischeBeschrijvingen,
+    FormeleStratigrafie,
+    GeotechnischeCodering,
+    QuartairStratigrafie,
+)
+from pydov.types.sondering import Sondering
 
 type_objects = [Boring,
+                Sondering,
                 GrondwaterFilter,
                 InformeleStratigrafie,
+                FormeleStratigrafie,
                 HydrogeologischeStratigrafie,
                 GecodeerdeLithologie,
-                LithologischeBeschrijvingen,]
+                LithologischeBeschrijvingen,
+                GeotechnischeCodering,
+                QuartairStratigrafie,]
 
 
 @pytest.mark.parametrize("objecttype", type_objects)
@@ -39,3 +50,39 @@ def test_get_fields_sourcexml(objecttype):
     fields = objecttype.get_fields(source=('xml',))
     for field in fields.values():
         assert field['source'] == 'xml'
+
+
+@pytest.mark.parametrize("objecttype", type_objects)
+def test_extend_fields_no_extra(objecttype):
+    """Test the extend_fields method for empty extra_fields.
+
+    Test whether the returned fields match the existing fields.
+    Test whether the returned fields are not the same fields as the original
+    fields.
+
+    """
+    fields = objecttype.extend_fields([])
+    assert fields == objecttype.fields
+    assert fields is not objecttype.fields
+
+
+@pytest.mark.parametrize("objecttype", type_objects)
+def test_extend_fields_with_extra(objecttype):
+    """Test the extend_fields method with extra_fields.
+
+    Test whether the extra field is included.
+
+    """
+    extra_fields = [
+        XmlField(name='grondwatersysteem',
+                 source_xpath='/filter/ligging/grondwatersysteem',
+                 definition='Grondwatersysteem waarin de filter hangt.',
+                 datatype='string',
+                 notnull=False)
+    ]
+
+    fields = objecttype.extend_fields(extra_fields)
+
+    assert len(fields) == len(objecttype.fields) + len(extra_fields)
+
+    assert fields[-1] == extra_fields[-1]
diff --git a/tests/test_types_pluggable.py b/tests/test_types_pluggable.py
new file mode 100644
index 0000000..34df73b
--- /dev/null
+++ b/tests/test_types_pluggable.py
@@ -0,0 +1,234 @@
+import pytest
+
+from owslib.fes import PropertyIsEqualTo
+from pydov.search.grondwaterfilter import GrondwaterFilterSearch
+from pydov.types.abstract import (
+    AbstractDovSubType,
+)
+from pydov.types.fields import XmlField
+from pydov.types.grondwaterfilter import GrondwaterFilter
+
+from tests.test_search import (
+    mp_wfs,
+    wfs,
+    mp_remote_md,
+    mp_remote_fc,
+    mp_remote_describefeaturetype,
+    mp_remote_wfs_feature,
+    mp_remote_xsd,
+    mp_dov_xml,
+    mp_dov_xml_broken,
+    wfs_getfeature,
+    wfs_feature,
+)
+
+location_md_metadata = 'tests/data/types/grondwaterfilter/md_metadata.xml'
+location_fc_featurecatalogue = \
+    'tests/data/types/grondwaterfilter/fc_featurecatalogue.xml'
+location_wfs_describefeaturetype = \
+    'tests/data/types/grondwaterfilter/wfsdescribefeaturetype.xml'
+location_wfs_getfeature = 'tests/data/types/grondwaterfilter/wfsgetfeature.xml'
+location_wfs_feature = 'tests/data/types/grondwaterfilter/feature.xml'
+location_dov_xml = 'tests/data/types/grondwaterfilter/grondwaterfilter.xml'
+location_xsd_base = 'tests/data/types/grondwaterfilter/xsd_*.xml'
+
+
+class MyGrondwaterFilter(GrondwaterFilter):
+
+    fields = GrondwaterFilter.extend_fields([
+        XmlField(name='grondwatersysteem',
+                 source_xpath='/filter/ligging/grondwatersysteem',
+                 definition='Grondwatersysteem waarin de filter hangt.',
+                 datatype='string')
+    ])
+
+
+class MyWrongGrondwaterFilter(GrondwaterFilter):
+
+    fields = GrondwaterFilter.extend_fields([
+        {'name': 'grondwatersysteem',
+         'source': 'xml',
+         'sourcefield': '/filter/ligging/grondwatersysteem',
+         'definition': 'Grondwatersysteem waarin de filter hangt.',
+         'type': 'string',
+         'notnull': False
+         }
+    ])
+
+
+class MyFilterOpbouw(AbstractDovSubType):
+
+    rootpath = './/filter/opbouw/onderdeel'
+
+    fields = [
+        XmlField(name='opbouw_van',
+                 source_xpath='/van',
+                 definition='Opbouw van',
+                 datatype='float'),
+        XmlField(name='opbouw_tot',
+                 source_xpath='/tot',
+                 definition='Opbouw tot',
+                 datatype='float'),
+        XmlField(name='opbouw_element',
+                 source_xpath='/filterelement',
+                 definition='Opbouw element',
+                 datatype='string',
+                 notnull=False)
+    ]
+
+
+class MyGrondwaterFilterOpbouw(GrondwaterFilter):
+
+    subtypes = [MyFilterOpbouw]
+
+
+class TestMyWrongGrondwaterFilter(object):
+    """Class grouping tests for the MyWrongGrondwaterFilter custom type."""
+    def test_get_fields(self):
+        """Test the get_fields method.
+
+        Test whether a RuntimeError is raised.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyWrongGrondwaterFilter)
+
+        with pytest.raises(RuntimeError):
+            fs.get_fields()
+
+    def test_search(self, mp_wfs, mp_remote_describefeaturetype,
+                    mp_remote_md, mp_remote_fc, mp_remote_wfs_feature,
+                    mp_dov_xml):
+        """Test the search method.
+
+        Test whether a RuntimeError is raised.
+
+        Parameters
+        ----------
+        mp_wfs : pytest.fixture
+            Monkeypatch the call to the remote GetCapabilities request.
+        mp_remote_describefeaturetype : pytest.fixture
+            Monkeypatch the call to a remote DescribeFeatureType.
+        mp_remote_md : pytest.fixture
+            Monkeypatch the call to get the remote metadata.
+        mp_remote_fc : pytest.fixture
+            Monkeypatch the call to get the remote feature catalogue.
+        mp_remote_wfs_feature : pytest.fixture
+            Monkeypatch the call to get WFS features.
+        mp_dov_xml : pytest.fixture
+            Monkeypatch the call to get the remote XML data.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyWrongGrondwaterFilter)
+
+        with pytest.raises(RuntimeError):
+            fs.search(query=PropertyIsEqualTo(
+                propertyname='filterfiche',
+                literal='https://www.dov.vlaanderen.be/data/'
+                        'filter/2003-004471'))
+
+
+class TestMyGrondwaterFilter(object):
+    """Class grouping tests for the MyGrondwaterFilter custom type."""
+    def test_get_fields(self):
+        """Test the get_fields method.
+
+        Test whether the extra field is available in the output of the
+        get_fields metadata.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilter)
+        fields = fs.get_fields()
+
+        assert 'grondwatersysteem' in fields
+
+    def test_search(self, mp_wfs, mp_remote_describefeaturetype,
+                    mp_remote_md, mp_remote_fc, mp_remote_wfs_feature,
+                    mp_dov_xml):
+        """Test the search method.
+
+        Test whether the extra fields from the custom type are resolved into
+        data in the result dataframe.
+
+        Parameters
+        ----------
+        mp_wfs : pytest.fixture
+            Monkeypatch the call to the remote GetCapabilities request.
+        mp_remote_describefeaturetype : pytest.fixture
+            Monkeypatch the call to a remote DescribeFeatureType.
+        mp_remote_md : pytest.fixture
+            Monkeypatch the call to get the remote metadata.
+        mp_remote_fc : pytest.fixture
+            Monkeypatch the call to get the remote feature catalogue.
+        mp_remote_wfs_feature : pytest.fixture
+            Monkeypatch the call to get WFS features.
+        mp_dov_xml : pytest.fixture
+            Monkeypatch the call to get the remote XML data.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilter)
+
+        df = fs.search(query=PropertyIsEqualTo(
+            propertyname='filterfiche',
+            literal='https://www.dov.vlaanderen.be/data/filter/2003-004471'))
+
+        assert 'grondwatersysteem' in df
+        assert df.iloc[0].grondwatersysteem == 'Centraal Vlaams Systeem'
+
+
+class TestMyGrondwaterFilterOpbouw(object):
+    """Class grouping tests for the MyGrondwaterFilterOpbouw and
+    MyFilterOpbouw custom type."""
+    def test_get_fields(self):
+        """Test the get_fields method.
+
+        Test whether the extra field is available in the output of the
+        get_fields metadata.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilterOpbouw)
+        fields = fs.get_fields()
+
+        assert 'datum' not in fields
+        assert 'peil_mtaw' not in fields
+
+        assert 'opbouw_van' in fields
+        assert 'opbouw_tot' in fields
+        assert 'opbouw_element' in fields
+
+    def test_search(self, mp_wfs, mp_remote_describefeaturetype,
+                    mp_remote_md, mp_remote_fc, mp_remote_wfs_feature,
+                    mp_dov_xml):
+        """Test the search method.
+
+        Test whether the extra fields from the custom type are resolved into
+        data in the result dataframe.
+
+        Parameters
+        ----------
+        mp_wfs : pytest.fixture
+            Monkeypatch the call to the remote GetCapabilities request.
+        mp_remote_describefeaturetype : pytest.fixture
+            Monkeypatch the call to a remote DescribeFeatureType.
+        mp_remote_md : pytest.fixture
+            Monkeypatch the call to get the remote metadata.
+        mp_remote_fc : pytest.fixture
+            Monkeypatch the call to get the remote feature catalogue.
+        mp_remote_wfs_feature : pytest.fixture
+            Monkeypatch the call to get WFS features.
+        mp_dov_xml : pytest.fixture
+            Monkeypatch the call to get the remote XML data.
+
+        """
+        fs = GrondwaterFilterSearch(objecttype=MyGrondwaterFilterOpbouw)
+
+        df = fs.search(query=PropertyIsEqualTo(
+            propertyname='filterfiche',
+            literal='https://www.dov.vlaanderen.be/data/filter/2003-004471'))
+
+        assert 'opbouw_van' in df
+        assert 'opbouw_tot' in df
+        assert 'opbouw_element' in df
+
+        assert df.iloc[-1].opbouw_van == 2.5
+        assert df.iloc[-1].opbouw_tot == 2.7
+        assert df.iloc[-1].opbouw_element == 'zandvang'