> For the complete documentation index, see [llms.txt](https://olivers-expression-lab.gitbook.io/filter-expressions-in-fme/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://olivers-expression-lab.gitbook.io/filter-expressions-in-fme/the-formula.md).

# The Formula

Let's have a look at the filter expression again:

```xml
<fes:Filter 
   xmlns:fes="http://www.opengis.net/fes/2.0"
   xmlns:cp="http://inspire.ec.europa.eu/schemas/cp/4.0">
   <fes:PropertyIsEqualTo>
      <fes:ValueReference>cp:nationalCadastralReference</fes:ValueReference>
      <fes:Literal>05430202600946______</fes:Literal>
   </fes:PropertyIsEqualTo>
</fes:Filter>
```

Maybe you're wondering about the prefixes

* fes:
* xmlns:
* cp:

What's this?

Did you ever hear about **XML n**ame**s**paces, aka xmlns?

A XML namespace is comparable to

* a dictionary or
* a telephone book

A telephone number is only valid in combination with an area code (which represents a certain city or geographic area).&#x20;

Let's give an example: the telephone number is 501 9985.

You may dial it on your phone and maybe someone answers (please don't do it in real!).

501 9985 without a prefix is not specific enough! You'll need an area code and maybe a country code in order to reach someone.

If you put the country code "+1" and the area code "604" for greater Vancouver in front of 501 9985 you'll get Safe Softwares phone number: +1 604 501 9985

Let's have a look at another example: take the word "Kindergarten"&#x20;

Does it occur in your dictionary? In german Duden dictionary it does. What does the word mean in your language?

W3schools says: "*XML Namespaces provide a method to avoid element name conflicts.*" It means they are used to define unique and precise names.

Name conflicts in XML can easily be avoided using a name prefix.

Use the following syntax to declare a namespace: **xmlns:prefix="URI"**, e.g.

* xmlns:fes="<http://www.opengis.net/fes/2.0>"
* xmlns:cp="<http://inspire.ec.europa.eu/schemas/cp/4.0>"

Please note that a namespaces points to an **URI** (**U**niform **R**esource **I**dentifier), not an **URL** (**U**niform **R**esource **L**ocator).

An URI may be a real URL (internet link) you can open using a browser, but it doesn't have to be!

Coming back to filter expressions, we have to define the correct namespace prefixes within our expression in order to get them working. But how to find them out?

I'll show you 2 possible ways to get the right namespaces.

1. Examine a feature
2. Examine the Translation Log

## Examine a feature

For the first way, we will use another WFS.

This time a service providing traffic meters for counting vehicles in North Rhine-Westphalia. The WFS URL is:

```url
https://www.wfs.nrw.de/wfs/strassen_nrw?SERVICE=WFS&VERSION=2.0.0
```

Add a WFS reader to your FME Workbench and configure it the following way:

<figure><img src="/files/jM8UfaSKznd9RIxCK1ZT" alt=""><figcaption></figcaption></figure>

Select **Zaehlstellen** (counting points) as **\[Feature Types]**.

Set **\[Max Features]** to **1** and run the reader (Run Just This). You should see a point feature east of Dortmund.

Switch to Translation Log.&#x20;

Scroll upwards and look for **\<WFS> GetFeature URL**.

<figure><img src="/files/xJcAn9QadKieePWif00I" alt=""><figcaption></figcaption></figure>

Click on the blue colored URL or copy it to your browser.

```
https://www.wfs.nrw.de/wfs/strassen_nrw?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=ms%3AZaehlstellen&NAMESPACES=xmlns%28ms%2Chttp%3A%2F%2Fmapserver.gis.umn.edu%2Fmapserver%29&OUTPUTFORMAT=application%2Fgml%2Bxml%3B%20version%3D3.2&COUNT=1
```

Now, you'll see one feature encoded as GML/XML in your browser:

```xml
<wfs:FeatureCollection xmlns:ms="http://mapserver.gis.umn.edu/mapserver" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mapserver.gis.umn.edu/mapserver https://www.wfs.nrw.de/wfs/strassen_nrw?SERVICE=WFS&VERSION=2.0.0&REQUEST=DescribeFeatureType&TYPENAME=ms:Zaehlstellen&OUTPUTFORMAT=application%2Fgml%2Bxml%3B%20version%3D3.2 http://www.opengis.net/wfs/2.0 http://www.wfs.nrw.de/schemas/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://www.wfs.nrw.de/schemas/gml/3.2.1/gml.xsd" timeStamp="2023-08-13T18:16:59" numberMatched="unknown" numberReturned="1" next="https://www.wfs.nrw.de/wfs/strassen_nrw?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=ms%3AZaehlstellen&NAMESPACES=xmlns(ms%2Chttp%3A%2F%2Fmapserver.gis.umn.edu%2Fmapserver)&OUTPUTFORMAT=application%2Fgml%2Bxml%3B%20version%3D3.2&COUNT=1&STARTINDEX=1">
<wfs:boundedBy>
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::25832">
<gml:lowerCorner>397181.079930 5708784.375433</gml:lowerCorner>
<gml:upperCorner>397181.079930 5708784.375433</gml:upperCorner>
</gml:Envelope>
</wfs:boundedBy>
<wfs:member>
<ms:Zaehlstellen gml:id="Zaehlstellen.4411120A4411111A">
<gml:boundedBy>
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::25832">
<gml:lowerCorner>397181.079930 5708784.375433</gml:lowerCorner>
<gml:upperCorner>397181.079930 5708784.375433</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
<ms:Shape>
<gml:Point gml:id="Zaehlstellen.4411120A4411111A.1" srsName="urn:ogc:def:crs:EPSG::25832">
<gml:pos>397181.079930 5708784.375433</gml:pos>
</gml:Point>
</ms:Shape>
<ms:ABS>4411120A4411111A</ms:ABS>
<ms:STAT>1782.000000000000000</ms:STAT>
<ms:ZSTNR>44112109</ms:ZSTNR>
<ms:ZSTART>manuelle Zählstelle (SVZ)</ms:ZSTART>
<ms:ZSTARTKNZ>03</ms:ZSTARTKNZ>
<ms:ERFASSART>Keine Werte (d.h. weder DZ, noch TM, noch MZ-Ergebnisse vorliegend)</ms:ERFASSART>
<ms:BZUSTAND>aktiv (in Betrieb)</ms:BZUSTAND>
<ms:NETZSTAND>20230630190000</ms:NETZSTAND>
</ms:Zaehlstellen>
</wfs:member>
</wfs:FeatureCollection>
```

We will concentrate on the counting point number "**ZSTNR**":

```
<ms:ZSTNR>44112109</ms:ZSTNR>
```

As you can see, the attribute comes already together with a namespace prefix: **ms:ZSTNR**

Perfect!&#x20;

The only thing we still need is the URI. Finding the URI is also easy! Just look at the xml header:

```
xmlns:ms="http://mapserver.gis.umn.edu/mapserver"
```

Putting the things together, we can modify the filter expression from above the following way:

```xml
<fes:Filter 
   xmlns:fes="http://www.opengis.net/fes/2.0"
   xmlns:ms="http://mapserver.gis.umn.edu/mapserver">
   <fes:PropertyIsEqualTo>
      <fes:ValueReference>ms:ZSTNR</fes:ValueReference>
      <fes:Literal>52082215</fes:Literal>
   </fes:PropertyIsEqualTo>
</fes:Filter>
```

Please use counting point **52082215**.

Modify your reader as follows by right clicking on it:

<figure><img src="/files/grK5FtzEMU5duEpN3WxD" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/791MiSb1urcq9WWwchOS" alt=""><figcaption></figcaption></figure>

Clear **\[Max Features]** and paste the new filter expression in **\[XML Filter Expression]** editor and run the Workbench.

As a result, you'll see a counting point in Bonn, near World Conference Center Bonn (WCCB).

<figure><img src="/files/KgnfYChA4n7YRyALyhtW" alt=""><figcaption></figcaption></figure>

Great! This was easy.

## Examine the Translation Log

Coming back to our cadastral parcel service, the things are a little bit more difficult.

Add or modify your cadastral parcel WFS reader the following way:

| Parameter     | Value                                                               |
| ------------- | ------------------------------------------------------------------- |
| WFS URL       | <https://www.wfs.nrw.de/geobasis/wfs_nw_inspire-flurstuecke_alkis?> |
| Feature Types | CadastralParcel                                                     |
| Max Features  | 1                                                                   |

Run the reader and inspect the Translation Log.

Look for **\<WFS> GetFeature URL**, which is:

```url
https://www.wfs.nrw.de:443/geobasis/wfs_nw_inspire-flurstuecke_alkis?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=cp%3ACadastralParcel&NAMESPACES=xmlns%28cp%2Chttp%3A%2F%2Finspire.ec.europa.eu%2Fschemas%2Fcp%2F4.0%29&OUTPUTFORMAT=application%2Fgml%2Bxml%3B%20version%3D3.2&COUNT=1
```

and open it in a browser.

Save the returned document and open it either in a browser or in a text editor (remember: it is a GML/XML document). It will look like this (some content is turned of "..." for a clearer view):

```xml
<wfs:FeatureCollection xmlns:base="http://inspire.ec.europa.eu/schemas/base/3.3" xmlns="http://inspire.ec.europa.eu/schemas/cp/4.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" timeStamp="2023-08-13T19:11:31.688+02:00" numberReturned="1" numberMatched="unknown" xsi:schemaLocation="http://inspire.ec.europa.eu/schemas/cp/4.0 http://www.wfs.nrw.de/aaa-suite/schema/inspire.ec.europa.eu/schemas/cp/4.0/CadastralParcels.xsd http://www.opengis.net/wfs/2.0 http://www.wfs.nrw.de/aaa-suite/schema/ogc/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://www.wfs.nrw.de/aaa-suite/schema/ogc/gml/3.2.1/gml.xsd">
<wfs:boundedBy>
<gml:Envelope srsName="http://www.opengis.net/def/crs/epsg/0/25832" srsDimension="2">
...
</gml:Envelope>
</wfs:boundedBy>
<wfs:member>
<CadastralParcel gml:id="CadastralParcel_05439400700069______">
<gml:identifier codeSpace="http://inspire.ec.europa.eu/ids">https://registry.gdi-de.org/id/de.nw.inspire.cp.alkis/CadastralParcel_05439400700069______</gml:identifier>
<areaValue uom="m2">35190.00</areaValue>
<beginLifespanVersion>2017-01-16T15:22:59Z</beginLifespanVersion>
<endLifespanVersion nilReason="other:unpopulated" xsi:nil="true"/>
<geometry>
...
</geometry>
<inspireId>
<base:Identifier>
<base:localId>CadastralParcel_05439400700069______</base:localId>
<base:namespace>https://registry.gdi-de.org/id/de.nw.inspire.cp.alkis</base:namespace>
</base:Identifier>
</inspireId>
<label>69</label>
<nationalCadastralReference>05439400700069______</nationalCadastralReference>
<referencePoint>
<gml:Point gml:id="o11001.id.1.ST_PointOnSurface__T_.position__.Geom_0" srsName="http://www.opengis.net/def/crs/epsg/0/25832" srsDimension="2">
<gml:pos>315035.325 5579575.262</gml:pos>
</gml:Point>
</referencePoint>
<validFrom>1974-01-01</validFrom>
<validTo nilReason="other:unpopulated" xsi:nil="true"/>
<zoning xlink:href="https://registry.gdi-de.org/id/de.nw.inspire.cp.alkis/CadastralZoning_054394007"/>
</CadastralParcel>
</wfs:member>
</wfs:FeatureCollection>
```

We concentrate on the cadastral parcel number attribute:&#x20;

```
<nationalCadastralReference>05439400700069______</nationalCadastralReference>
```

As you can see, it comes **without** a namespace prefix, while we're expecting **cp:nationalCadastralReference**.

What a bummer! Well then?<br>

We may inspect the header where we will find&#x20;

```url
http://www.wfs.nrw.de/aaa-suite/schema/inspire.ec.europa.eu/schemas/cp/4.0/CadastralParcels.xsd
```

This URL points to a XML schema (\*.xsd). It can also be opened with a browser or text editor.

<figure><img src="/files/CHOEQOCPcVCwgOc0c84b" alt=""><figcaption></figcaption></figure>

If you're familiar with XML schemata you may recognize, that the cp:-prefix is passed to **nationalCadastralReference** from **BasicPropertyUnitType** which is a higher-order complex type.

**But, never mind!**

We will ask our best friend: the Translation Log!

First, we will inspect the feature in **Visual Preview** (or **Inspector**)

<figure><img src="/files/sKlvVPccb0eEsAfJyz20" alt=""><figcaption></figcaption></figure>

Next open **\[Columns...]**

<figure><img src="/files/oxwk5HcVLiGXfBoaNjZ3" alt=""><figcaption></figcaption></figure>

On the left side scroll down to the end of the list of available columns and activate **xml\_ns\_uri**.

<figure><img src="/files/Od9831KyfPSgVv5ec5hP" alt=""><figcaption></figcaption></figure>

For a better view, you may move it up near **nationalCadastralReference**.

Close the dialog and inspect the new attribute:&#x20;

<figure><img src="/files/gLYDqFYHkMwiXCFBTtIq" alt=""><figcaption></figcaption></figure>

```
http://inspire.ec.europa.eu/schemas/cp/4.0
```

Keep this in mind and switch to Translation Log.

Scroll up until you see "**Using prefix ...**" and look for the URI in question.

<figure><img src="/files/9T0oqqaoP7q6SWhA5V8i" alt=""><figcaption></figcaption></figure>

```
Using prefix 'cp' for uri 'http://inspire.ec.europa.eu/schemas/cp/4.0'
```

Surprise: the correspondig prefix is **cp**!

How cool is this!?

To complete the formula, we have to discuss **versions**.

## Choosing the right version

While treating GetCapabilities I mentioned, that you should use version 2.0.0 if your WFS supports this version.

Why?

Because there are some differences between&#x20;

* version 1.0.0, 1.1.0
* version 2.0

While we used **\<fes:ValueReference>** to query version 2.0 WFS, \
we have to use **\<ogc:PropertyName>** for version 1.x

| Version | Element            | Schema                                             |
| ------- | ------------------ | -------------------------------------------------- |
| 1.0.0   | ogc:PropertyName   | \[FMEdir]\xml\schemas\filter\\**1.0.0**\filter.xsd |
| 1.1.0   | ogc:PropertyName   | \[FMEdir]\xml\schemas\filter\\**1.1.0**\filter.xsd |
| 2.0.0   | fes:ValueReference | \[FMEdir]\xml\schemas\filter\\**2.0.0**\filter.xsd |

You may have a look at the corresponding schema called filter.xsd.

It can be found in FMEs installation directory (FMEdir) on your local hard drive.

Another little but tricky difference concerns the defintion of wildcards when using **PropertyIsLike**:

| Version | Expression | Expression | Expression                                 |
| ------- | ---------- | ---------- | ------------------------------------------ |
| 1.0.0   | wildCard   | singleChar | <mark style="color:red;">**escape**</mark> |
| 1.1.0   | wildCard   | singleChar | escapeChar                                 |
| 2.0.0   | wildCard   | singleChar | escapeChar                                 |

We will discuss the usage of **PropertyIsLike** later on.

To summarize let's have a look at our traffic meter WFS from above:

| Parameter      | Value                                      |
| -------------- | ------------------------------------------ |
| WFS URL        | <https://www.wfs.nrw.de/wfs/strassen_nrw?> |
| Version        | **1.0.0**  and **2.0.0**                   |
| Features Types | **Zaehlstellen** (*counting points*)       |
| Max Features   | leave blank                                |

If you want to query the service in **version 1.0.0/1.1.0** the filter expression should look like this:

```
<ogc:Filter 
   xmlns:ogc="http://www.opengis.net/ogc"
   xmlns:ms="http://mapserver.gis.umn.edu/mapserver">
   <ogc:PropertyIsEqualTo>
      <ogc:PropertyName>ms:ZSTNR</ogc:PropertyName>
      <ogc:Literal>52082215</ogc:Literal>
   </ogc:PropertyIsEqualTo>
</ogc:Filter>
```

and in **version 2.0.0** like this:

```
<fes:Filter 
   xmlns:fes="http://www.opengis.net/fes/2.0"
   xmlns:ms="http://mapserver.gis.umn.edu/mapserver">
   <fes:PropertyIsEqualTo>
      <fes:ValueReference>ms:ZSTNR</fes:ValueReference>
      <fes:Literal>52082215</fes:Literal>
   </fes:PropertyIsEqualTo>
</fes:Filter>
```

**Caution**: You may notice that \<fes:ValueReference> works with version 1.0.0/1.1.0 as well!

This is usually the case, if the WFS supports both, 1.x and 2.0.

But if you stumble on a version 1.x only WFS, please keep this in mind!

Ok, now we know how to collect the information we need to write perfect filter expressions.

Let's put the theory into practice!

## Good to know #3

Sometimes it is a good idea to check if your **XML** Filter Expression **is well formed**, i.e. if the syntax is correct.

Especially if you copy & paste some code from the internet it may happen that quotation marks or something else gets lost.

There are tons of XML validation tools in the internet. My personal favorite is&#x20;

```url
https://jsonformatter.org/xml-validator
```

<figure><img src="/files/FjMHGySzGxekKSO6hzF9" alt=""><figcaption></figcaption></figure>

Q: What's going wrong here?

A: The closing quotation mark is missing!
