Skip to content

Commit 2ba5736

Browse files
committed
#2 Adds support for JSON files
1 parent cde3ed0 commit 2ba5736

10 files changed

+226
-96
lines changed

README.md

+42-55
Original file line numberDiff line numberDiff line change
@@ -10,87 +10,74 @@
1010
</div>
1111
<br>
1212

13-
Wireshark-like display filter for python dictionaries.
13+
A Wireshark-like display filter for Python dictionaries. This tool allows you to easily filter, analyze, and
14+
manipulate data in Python dictionaries. It offers a range of features including comparison operators,
15+
combining operators, membership operators, and more.
16+
17+
## Table of Contents
18+
1. [Quick Start](#quick-start)
19+
2. [Usage](#usage)
20+
3. [Features](#features)
21+
4. [Examples](#examples)
22+
5. [Acknowledgements](#acknowledgements)
23+
24+
## Quick Start
25+
26+
Here's a simple example to get you started. First, install the package:
1427

15-
## Setup
1628
```commandline
1729
pip3 install python-dict-display-filter
1830
```
1931

20-
## Usage
32+
Then, use it to filter a list of dictionaries:
33+
```
34+
from pydictdisplayfilter import DictDisplayFilter
35+
actors = [
36+
{"name": ["Laurence", "Fishburne"], "age": {"born": "1961"}, "gender": "male"},
37+
{"name": ["Keanu", "Reeves"], "age": {"born": "1964"}, "gender": "male", "power": ["flight", "bullet-time"]},
38+
{"name": ["Joe", "Pantoliano"], "age": {"born": "1951"}, "gender": "male"},
39+
{"name": ["Carrie-Anne", "Moss"], "age": {"born": "1967"}, "gender": "female"}
40+
]
41+
ddf = DictDisplayFilter(actors)
2142
22-
The basics and the syntax of the display filter are described in the
23-
<a href="https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/USER_GUIDE.md">User Guide</a>.
43+
# This will filter the list to show only male actors born between 1960 and 1965 whose names end with 'e'
44+
filtered_actors = ddf.filter("gender == male and (age.born > 1960 and age.born < 1965) and name matches .*e$")
2445
25-
If you want to see some advanced examples of how ```python-dict-display-filter``` can be put to use checkout the
26-
<a href="https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/EXAMPLES.md">Examples</a>.
46+
print(list(filtered_actors))
47+
[{'name': ['Laurence', 'Fishburne'], 'age': {'born': '1961'}, 'gender': 'male'}]
48+
```
2749

28-
If you want to use ```python-dict-display-filter``` in your own application and customize it to your needs
29-
check out the
30-
<a href="https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/DEVELOPER_GUIDE.md">Developer Guide</a>.
50+
For more details, please refer to the
51+
<a href="https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/USER_GUIDE.md">User Guide</a>.
3152

3253
## Features
33-
The following overview shows all the supported features of the display filter:
54+
55+
Python Dictionary Display Filter supports a wide range of features, including:
3456
* **Comparison Operators:** ```==```, ```!=```, ```<=```, ```<```, ```>=```, ```>```, ```~=```, ```~```, ```&```
3557
* **Combining Operators:** ```and```, ```or```, ```xor```, ```not```
3658
* **Membership Operators:** ```in```
3759
* **Types:** ```Text```, ```Number```, ```Date & Time```, ```Ethernet-```, ```IPv4-```, ```IPv6-Address```
3860
* **Slicing:** ```Text```, ```Ethernet-```, ```IPv4-```, ```IPv6-Address```
3961
* **Functions:** ```upper```, ```lower```, ```len```
62+
* **Data Sources**: ```CSV```, ```Dictionaries```, ```JSON```, ```SQLite```
4063

4164
For a detailed description of the individual features check out the
4265
<a href="https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/USER_GUIDE.md">User Guide</a>.
4366

4467
## Examples
4568

46-
Initialize ```DictDisplayFilter``` with a list of dictionaries:
47-
```
48-
> from pydictdisplayfilter import DictDisplayFilter
49-
> actors = [
50-
{"name": ["Laurence", "Fishburne"], "age": {"born": "1961"}, "gender": "male"},
51-
{"name": ["Keanu", "Reeves"], "age": {"born": "1964"}, "gender": "male", "power": ["flight", "bullet-time"]},
52-
{"name": ["Joe", "Pantoliano"], "age": {"born": "1951"}, "gender": "male"},
53-
{"name": ["Carrie-Anne", "Moss"], "age": {"born": "1967"}, "gender": "female"}
54-
]
55-
> ddf = DictDisplayFilter(actors)
56-
```
57-
58-
Show only actors with some kind of super-power:
59-
```
60-
> ddf.filter("power")
61-
```
62-
63-
Show only actors which were born before 1965:
64-
```
65-
> ddf.filter("age.born < 1965")
66-
```
67-
68-
Show only female actors:
69-
```
70-
> ddf.filter("gender == female")
71-
```
72-
73-
Show all male actors which are born between 1960 and 1965:
74-
```
75-
> ddf.filter("gender == male and (age.born > 1960 and age.born < 1965)")
76-
```
77-
78-
Show all actors which name contain the character 'e':
79-
```
80-
> ddf.filter("name contains e")
81-
```
82-
83-
Show all actors which name matches a regular expression:
84-
```
85-
> ddf.filter("name matches .*e$")
86-
```
69+
For detailed examples of how the display filter can be utilized, please refer to the following:
8770

88-
## Inspired by
71+
* [CSV Display Filter](https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/EXAMPLES.md#csv-display-filter)
72+
* [JSON Display Filter](https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/EXAMPLES.md#json-display-filter)
73+
* [SQLite Display Filter](https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/EXAMPLES.md#sqlite-display-filter)
74+
* [Nmap Display Filter](https://github.com/bytebutcher/python-dict-display-filter/blob/main/docs/EXAMPLES.md#nmap-display-filter)
8975

90-
* <a href="https://wiki.wireshark.org/DisplayFilters">Wireshark Display Filter</a>
76+
## Acknowledgements
9177

92-
## Powered by
78+
This project wouldn't be possible without these awesome projects:
9379

80+
* <a href="https://wiki.wireshark.org/DisplayFilters">wireshark display filter</a>: Display filter for filtering network packages
9481
* <a href="https://github.com/wolever/parameterized">parameterized</a>: Parameterized testing with any Python test framework
9582
* <a href="https://github.com/pyparsing/pyparsing/">pyparsing</a>: Creating PEG-parsers made easy
9683
* <a href="https://github.com/bytebutcher/ipranger/">ipranger</a>: Parsing and matching IPv4-addresses

data/csv_example.csv

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
"name","age","gender","killed"
2-
"Morpheus",38,"male",False
3-
"Neo",35,"male",False
4-
"Cipher",48,"male",True
5-
"Trinity",32,"female",False
1+
"name","actor","age","gender","killed"
2+
"Morpheus","Laurence Fishburne",38,"male",False
3+
"Neo","Keanu Reeves",35,"male",False
4+
"Cipher","Joe Pantoliano",48,"male",True
5+
"Trinity","Carrie-Anne Moss",32,"female",False

data/json_example.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
{"name": "Morpheus","actor": ["Laurence", "Fishburne"], "age": {"born": "1961"}, "gender": "male"},
3+
{"name": "Neo","actor": ["Keanu", "Reeves"], "age": {"born": "1964"}, "gender": "male", "power": ["flight", "bullet-time"]},
4+
{"name": "Cipher","actor": ["Joe", "Pantoliano"], "age": {"born": "1951"}, "gender": "male"},
5+
{"name": "Trinity","actor": ["Carrie-Anne", "Moss"], "age": {"born": "1967"}, "gender": "female"}
6+
]

data/sqlite_example.sqlite

4 KB
Binary file not shown.

docs/DEVELOPER_GUIDE.md

+2
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,7 @@ The ```python-dict-display-filter``` defines a set of helpers which may be used
3434

3535
| Helper Class | Description |
3636
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
37+
| ```DisplayFilterShell``` | A command line loop which allows to filter data on a provided data store. Uses Table to print the result in a pretty table. |
3738
| ```DictDisplayFilterShell``` | A command line loop which allows to filter data on a provided data store. Uses DictTable to print the result in a pretty table. |
39+
| ```Table``` | Initialized with a data store, provides a filter method, prints results in a pretty table. |
3840
| ```DictTable``` | Initialized with a data store, provides a filter method, prints results in a pretty table. |

docs/EXAMPLES.md

+57-15
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@
55
<h1 align="center" style="margin-top: 0px;">Examples</h1>
66
<br>
77

8+
## Table of Contents
9+
10+
1. [CSV Display Filter](#csv-display-filter)
11+
2. [JSON Display Filter](#json-display-filter)
12+
3. [Nmap Display Filter](#nmap-display-filter)
13+
4. [SQLite Display Filter](#sqlite-display-filter)
14+
815
## CSV Display Filter
916

10-
This example shows how to create a display filter for CSV-files:
17+
This example shows how to use the display filter to query CSV-files:
1118
```commandline
1219
python3 examples/csv_display_filter.py data/example.csv
1320
# Enter ?help for a list of commands.
1421
```
1522
```
1623
> fields
1724
name
25+
actor
1826
age
1927
gender
2028
killed
@@ -29,38 +37,36 @@ Neo | 35 | male | False
2937
1 row in set (0.01 secs)
3038
```
3139

32-
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/csv_display_filter.py">examples/csv_display_filter.py</a> for the actual source code.
40+
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/csv_display_filter.py">examples/csv_display_filter.py</a> for implementation details.
3341

3442

35-
# SQLite Display Filter
43+
## JSON Display Filter
3644

37-
This example shows how to create a display filter for a sqlite database:
45+
This example shows how to use the display filter to query JSON files:
3846
```commandline
39-
python3 examples/sqlite_display_filter.py data/example.sqlite
47+
python3 examples/json_display_filter.py data/example.json
4048
# Enter ?help for a list of commands.
4149
```
4250
```
4351
> fields
44-
name
45-
age
4652
gender
47-
killed
53+
name
54+
power
55+
actor
56+
age.born
4857
```
4958
```
5059
> filter name == Neo
51-
52-
name | age | gender | killed
53-
---- | --- | ------ | ------
54-
Neo | 35 | male | 0
60+
{"name": "Neo", "actor": ["Keanu", "Reeves"], "age": {"born": "1964"}, "gender": "male", "power": ["flight", "bullet-time"]}
5561
5662
1 row in set (0.01 secs)
5763
```
5864

59-
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/sqlite_display_filter.py">examples/sqlite_display_filter.py</a> for the actual source code.
65+
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/json_display_filter.py">examples/json_display_filter.py</a> for implementation details.
6066

6167
## Nmap Display Filter
6268

63-
This example shows how to create a display filter for nmap-xml-files:
69+
This example shows how to use a display filter to query Nmap XML files:
6470
```commandline
6571
python3 examples/nmap_display_filter.py data/nmap_example.xml
6672
# Enter ?help for a list of commands.
@@ -98,4 +104,40 @@ host | port | protocol | status | service
98104
6 rows in set (0.01 secs)
99105
```
100106

101-
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/nmap_display_filter.py">examples/nmap_display_filter.py</a> for the actual source code.
107+
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/nmap_display_filter.py">examples/nmap_display_filter.py</a> for implementation details.
108+
109+
## SQLite Display Filter
110+
111+
This example shows how to use the display filter to query a SQLite database:
112+
```commandline
113+
python3 examples/sqlite_display_filter.py data/example.sqlite
114+
# Enter ?help for a list of commands.
115+
```
116+
```
117+
> tables
118+
People
119+
```
120+
```
121+
> use People
122+
Database changed
123+
```
124+
```
125+
> fields
126+
name
127+
actor
128+
age
129+
gender
130+
killed
131+
```
132+
133+
```
134+
> filter name == Neo
135+
136+
name | actor | age | gender | killed
137+
---- | ------------ | --- | ------ | ------
138+
Neo | Keanu Reeves | 35 | male | 0
139+
140+
1 row in set (0.01 secs)
141+
```
142+
143+
See <a href="https://github.com/bytebutcher/python-dict-display-filter/raw/main/examples/sqlite_display_filter.py">examples/sqlite_display_filter.py</a> for implementation details.

examples/json_display_filter.py

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# vim: ts=8:sts=8:sw=8:noexpandtab
2+
#
3+
# This file is part of python-dict-display-filter.
4+
#
5+
# This program is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation; either version 2 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
import argparse
18+
import json
19+
import logging
20+
import os.path
21+
import sys
22+
import traceback
23+
from typing import List, Set
24+
25+
from pydictdisplayfilter import DictDisplayFilter
26+
from pydictdisplayfilter.helpers import Table, DisplayFilterShell
27+
28+
29+
def read_json_file(json_file):
30+
try:
31+
with open(json_file, mode='r') as infile:
32+
return json.load(infile)
33+
except Exception as err:
34+
logger.debug(err)
35+
raise Exception("{}: Error reading file".format(json_file))
36+
37+
38+
class JSONTable(Table):
39+
""" Data store with filter capabilities and pretty table printout. """
40+
41+
def __init__(self, data_store: List[dict]):
42+
""" Initializes the DictTable with a data store. """
43+
self._data_store = data_store
44+
super().__init__(DictDisplayFilter(data_store))
45+
46+
def _make_table(self, data_store: List[dict]) -> List[str]:
47+
""" Creates a view for nested data items. """
48+
return [json.dumps(data) for data in data_store]
49+
50+
def _extract_keys(self, data_store: List[dict]) -> List[str]:
51+
""" Extracts the keys from a nested data store. """
52+
def extract_keys(data: dict, parent_key: str='') -> Set[str]:
53+
keys = set()
54+
if isinstance(data, dict):
55+
for k, v in data.items():
56+
new_key = f"{parent_key}.{k}" if parent_key else k
57+
keys |= extract_keys(v, new_key)
58+
else:
59+
keys.add(parent_key)
60+
return keys
61+
62+
all_keys = set()
63+
for data in data_store:
64+
all_keys |= extract_keys(data)
65+
return list(all_keys)
66+
67+
def fields(self, thorough: bool = True) -> List[str]:
68+
""" Returns the field names used in the data store. """
69+
if not self._data_store:
70+
# No items in data store, so there are no fields to query either.
71+
return list()
72+
return self._extract_keys(self._data_store)
73+
74+
75+
class JSONDisplayFilterShell(DisplayFilterShell):
76+
""" A little shell for querying a list of dictionaries using the display filter. """
77+
78+
def __init__(self, data_store: List[dict]):
79+
""" Initializes the DictDisplayFilterShell with a data store. """
80+
super().__init__(JSONTable(data_store))
81+
82+
83+
if __name__ == '__main__':
84+
logging.basicConfig(format='%(message)s')
85+
logger = logging.getLogger(__name__)
86+
parser = argparse.ArgumentParser(description='Display filter for json file.')
87+
parser.add_argument('json_file', action='store', metavar='FILE', help='JSON-file to load')
88+
89+
# Parse all arguments
90+
arguments = parser.parse_args()
91+
92+
json_file = arguments.json_file
93+
if not os.path.isfile(json_file):
94+
logger.error("{}: No such file".format(json_file))
95+
sys.exit(1)
96+
97+
try:
98+
data_store = read_json_file(json_file)
99+
JSONDisplayFilterShell(data_store).cmdloop()
100+
except Exception as err:
101+
logger.error(str(err))
102+
traceback.print_exc()
103+
sys.exit(1)

examples/nmap_display_filter.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import sys
2121
import traceback
2222
from collections import defaultdict
23-
from typing import List, Dict
23+
from typing import List
2424

2525
from pydictdisplayfilter.helpers import DictDisplayFilterShell
2626

0 commit comments

Comments
 (0)