jsonstat-validator 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. jsonstat_validator/__init__.py +34 -0
  2. jsonstat_validator/tests/__init__.py +1 -0
  3. jsonstat_validator/tests/conftest.py +10 -0
  4. jsonstat_validator/tests/custom_tests.py +411 -0
  5. jsonstat_validator/tests/samples/areas/canada.json +16 -0
  6. jsonstat_validator/tests/samples/areas/galicia.json +16 -0
  7. jsonstat_validator/tests/samples/areas/index.json +31 -0
  8. jsonstat_validator/tests/samples/areas/oecd.json +16 -0
  9. jsonstat_validator/tests/samples/areas/us.json +26 -0
  10. jsonstat_validator/tests/samples/canada.json +131 -0
  11. jsonstat_validator/tests/samples/datasets/index.json +51 -0
  12. jsonstat_validator/tests/samples/galicia.json +124 -0
  13. jsonstat_validator/tests/samples/hierarchy.json +200 -0
  14. jsonstat_validator/tests/samples/index.json +36 -0
  15. jsonstat_validator/tests/samples/metrics/gdp/gsp.json +16 -0
  16. jsonstat_validator/tests/samples/metrics/gdp/gsppc.json +16 -0
  17. jsonstat_validator/tests/samples/metrics/gdp/gspw.json +16 -0
  18. jsonstat_validator/tests/samples/metrics/gdp/index.json +26 -0
  19. jsonstat_validator/tests/samples/metrics/index.json +26 -0
  20. jsonstat_validator/tests/samples/metrics/lfs/employed.json +16 -0
  21. jsonstat_validator/tests/samples/metrics/lfs/index.json +31 -0
  22. jsonstat_validator/tests/samples/metrics/lfs/lf.json +16 -0
  23. jsonstat_validator/tests/samples/metrics/lfs/unemployed.json +16 -0
  24. jsonstat_validator/tests/samples/metrics/lfs/unr.json +21 -0
  25. jsonstat_validator/tests/samples/metrics/pop/index.json +21 -0
  26. jsonstat_validator/tests/samples/metrics/pop/pop.json +26 -0
  27. jsonstat_validator/tests/samples/metrics/pop/popw.json +16 -0
  28. jsonstat_validator/tests/samples/oecd.json +170 -0
  29. jsonstat_validator/tests/samples/order.json +38 -0
  30. jsonstat_validator/tests/samples/sources/bls.json +21 -0
  31. jsonstat_validator/tests/samples/sources/ige.json +16 -0
  32. jsonstat_validator/tests/samples/sources/index.json +41 -0
  33. jsonstat_validator/tests/samples/sources/jsonstat.json +21 -0
  34. jsonstat_validator/tests/samples/sources/oecd.json +16 -0
  35. jsonstat_validator/tests/samples/sources/statcan.json +16 -0
  36. jsonstat_validator/tests/samples/sources/wikipedia.json +16 -0
  37. jsonstat_validator/tests/samples/topics/accounts.json +16 -0
  38. jsonstat_validator/tests/samples/topics/demos.json +21 -0
  39. jsonstat_validator/tests/samples/topics/index.json +31 -0
  40. jsonstat_validator/tests/samples/topics/labor.json +26 -0
  41. jsonstat_validator/tests/samples/topics/population.json +26 -0
  42. jsonstat_validator/tests/samples/us-gsp.json +230 -0
  43. jsonstat_validator/tests/samples/us-labor.json +6509 -0
  44. jsonstat_validator/tests/samples/us-unr.json +3269 -0
  45. jsonstat_validator/tests/test_official_samples.py +51 -0
  46. jsonstat_validator/validator.py +608 -0
  47. jsonstat_validator-0.1.0.dist-info/LICENSE +21 -0
  48. jsonstat_validator-0.1.0.dist-info/METADATA +196 -0
  49. jsonstat_validator-0.1.0.dist-info/RECORD +51 -0
  50. jsonstat_validator-0.1.0.dist-info/WHEEL +5 -0
  51. jsonstat_validator-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,34 @@
1
+ """
2
+ JSON-stat validator.
3
+
4
+ A validator for the JSON-stat 2.0 format, a simple lightweight format for data
5
+ interchange. It provides a way to exchange data associated to dimensions,
6
+ following the cube model that is so common in statistical offices.
7
+
8
+ For more information on JSON-stat, see: https://json-stat.org/
9
+ """
10
+
11
+ from jsonstat_validator.validator import (
12
+ Category,
13
+ Collection,
14
+ Dataset,
15
+ DatasetRole,
16
+ Dimension,
17
+ JSONStatSchema,
18
+ Link,
19
+ Unit,
20
+ validate_jsonstat,
21
+ )
22
+
23
+ __version__ = "0.1.0"
24
+ __all__ = [
25
+ "Dataset",
26
+ "Dimension",
27
+ "Collection",
28
+ "Link",
29
+ "Unit",
30
+ "Category",
31
+ "DatasetRole",
32
+ "JSONStatSchema",
33
+ "validate_jsonstat",
34
+ ]
@@ -0,0 +1 @@
1
+ """Test package for JSON-stat validator."""
@@ -0,0 +1,10 @@
1
+ """Configuration file for pytest."""
2
+
3
+ import pytest
4
+
5
+
6
+ def pytest_collection_modifyitems(items):
7
+ """Add custom markers to test items."""
8
+ for item in items:
9
+ if "samples" in item.nodeid:
10
+ item.add_marker(pytest.mark.samples)
@@ -0,0 +1,411 @@
1
+ """Custom test cases for the JSON-stat validator tool."""
2
+
3
+ import copy
4
+ from datetime import datetime
5
+
6
+ import pytest
7
+
8
+ from jsonstat_validator import Collection, Dataset, Dimension, validate_jsonstat
9
+
10
+
11
+ # --- Helper functions ---
12
+ def current_iso8601():
13
+ """Return the current ISO 8601 datetime as a string."""
14
+ return datetime.now().isoformat()
15
+
16
+
17
+ # --- Valid base structures ---
18
+ MINIMAL_DATASET = {
19
+ "version": "2.0",
20
+ "class": "dataset",
21
+ "id": ["time", "geo"],
22
+ "size": [2, 3],
23
+ "value": [1, 2, 3, 4, 5, 6],
24
+ "dimension": {
25
+ "time": {"category": {"index": ["2020", "2021"]}},
26
+ "geo": {"category": {"index": {"US": 0, "EU": 1, "AS": 2}}},
27
+ },
28
+ }
29
+
30
+ MINIMAL_DIMENSION = {
31
+ "version": "2.0",
32
+ "class": "dimension",
33
+ "category": {"index": ["male", "female"]},
34
+ }
35
+
36
+ MINIMAL_COLLECTION = {
37
+ "version": "2.0",
38
+ "class": "collection",
39
+ "link": {
40
+ "item": [
41
+ {
42
+ "class": "dataset",
43
+ "href": "https://json-stat.org/samples/oecd.json",
44
+ "label": "Unemployment rate in the OECD countries 2003-2014",
45
+ }
46
+ ]
47
+ },
48
+ }
49
+
50
+
51
+ class TestValidCases:
52
+ """Test cases for valid JSON-stat objects."""
53
+
54
+ def test_minimal_dataset(self):
55
+ """Test that a minimal dataset validates successfully."""
56
+ assert validate_jsonstat(MINIMAL_DATASET) is True
57
+
58
+ def test_sparse_dataset_values(self):
59
+ """Test that a dataset with sparse values validates successfully."""
60
+ data = copy.deepcopy(MINIMAL_DATASET)
61
+ data["value"] = {
62
+ "0:0": 1,
63
+ "0:1": 2,
64
+ "0:2": 3,
65
+ "1:0": 4,
66
+ "1:1": 5,
67
+ "1:2": 6,
68
+ }
69
+ assert validate_jsonstat(data) is True
70
+
71
+ def test_dataset_with_roles(self):
72
+ """Test that a dataset with roles validates successfully."""
73
+ data = copy.deepcopy(MINIMAL_DATASET)
74
+ data["role"] = {
75
+ "time": ["time"],
76
+ "geo": ["geo"],
77
+ }
78
+ assert validate_jsonstat(data) is True
79
+
80
+ def test_dimension_with_label(self):
81
+ """Test that a dimension with a label validates successfully."""
82
+ data = copy.deepcopy(MINIMAL_DIMENSION)
83
+ data["label"] = "Gender"
84
+ data["category"]["label"] = {"male": "Male", "female": "Female"}
85
+ assert validate_jsonstat(data) is True
86
+
87
+ def test_collection_with_nested_items(self):
88
+ """Test that a collection with nested items validates successfully."""
89
+ data = copy.deepcopy(MINIMAL_COLLECTION)
90
+ data["link"]["item"].append(
91
+ {
92
+ "class": "collection",
93
+ "href": "https://json-stat.org/samples/collection.json",
94
+ "label": "A nested collection",
95
+ }
96
+ )
97
+ assert validate_jsonstat(data) is True
98
+
99
+
100
+ class TestInvalidCases:
101
+ """Test cases for invalid JSON-stat objects."""
102
+
103
+ def test_missing_required_field(self):
104
+ """Test that a dataset missing a required field fails validation."""
105
+ data = copy.deepcopy(MINIMAL_DATASET)
106
+ del data["id"]
107
+ with pytest.raises(ValueError):
108
+ validate_jsonstat(data)
109
+
110
+ def test_size_length_mismatch(self):
111
+ """Test that a dataset with mismatched size and id length fails validation."""
112
+ data = copy.deepcopy(MINIMAL_DATASET)
113
+ data["size"] = [2, 3, 4] # One more than id length
114
+ with pytest.raises(ValueError):
115
+ validate_jsonstat(data)
116
+
117
+ def test_invalid_status_format(self):
118
+ """Test that a dataset with an invalid status format fails validation."""
119
+ data = copy.deepcopy(MINIMAL_DATASET)
120
+ data["status"] = ["a", "b", "c"] # Too few elements
121
+ with pytest.raises(ValueError):
122
+ validate_jsonstat(data)
123
+
124
+ def test_missing_dimension_definition(self):
125
+ """Test that a dataset with a missing dimension definition fails validation."""
126
+ data = copy.deepcopy(MINIMAL_DATASET)
127
+ data["id"] = ["time", "geo", "metric"] # Added a new dimension
128
+ data["size"] = [2, 3, 2] # Updated size accordingly
129
+ # But the dimension is not defined
130
+ with pytest.raises(ValueError):
131
+ validate_jsonstat(data)
132
+
133
+ def test_invalid_collection_link_key(self):
134
+ """Test that a collection with an invalid link key fails validation."""
135
+ data = copy.deepcopy(MINIMAL_COLLECTION)
136
+ # Collections must use 'item' as the relation type
137
+ data["link"] = {"invalid_key": data["link"]["item"]}
138
+ with pytest.raises(ValueError):
139
+ validate_jsonstat(data)
140
+
141
+ def test_unit_missing_decimals(self):
142
+ """Test that a unit missing the required decimals field fails validation."""
143
+ dimension = copy.deepcopy(MINIMAL_DIMENSION)
144
+ dimension["category"]["unit"] = {"male": {"label": "Count"}}
145
+ with pytest.raises(ValueError):
146
+ validate_jsonstat(dimension)
147
+
148
+ def test_category_missing_index_and_label(self):
149
+ """Test that a category missing both index and label fails validation."""
150
+ dimension = copy.deepcopy(MINIMAL_DIMENSION)
151
+ # Remove index and don't provide label
152
+ del dimension["category"]["index"]
153
+ with pytest.raises(ValueError):
154
+ validate_jsonstat(dimension)
155
+
156
+
157
+ class TestTypeValidation:
158
+ """Test cases for type validation in JSON-stat objects."""
159
+
160
+ @pytest.mark.parametrize(
161
+ "field,value",
162
+ [
163
+ ("version", 2.0), # Should be a string
164
+ ("class", "invalid_class"), # Invalid class value
165
+ ("size", ["not_number"]), # Should be integers
166
+ ("id", "not_list"), # Should be a list
167
+ ("value", "not_array_or_dict"), # Should be array or dict
168
+ ],
169
+ )
170
+ def test_invalid_types(self, field, value):
171
+ """Test that invalid types fail validation."""
172
+ data = copy.deepcopy(MINIMAL_DATASET)
173
+ data[field] = value
174
+ with pytest.raises(ValueError):
175
+ validate_jsonstat(data)
176
+
177
+ def test_link_validation(self):
178
+ """Test that link validation works correctly."""
179
+ collection = copy.deepcopy(MINIMAL_COLLECTION)
180
+ # Invalid URL format
181
+ collection["link"]["item"][0]["href"] = "invalid-url"
182
+ with pytest.raises(ValueError):
183
+ validate_jsonstat(collection)
184
+
185
+
186
+ class TestClassValues:
187
+ """Test cases for class values in JSON-stat objects."""
188
+
189
+ @pytest.mark.parametrize(
190
+ "model_class,input_data,expected",
191
+ [
192
+ (Dataset, MINIMAL_DATASET, "dataset"),
193
+ (Dimension, MINIMAL_DIMENSION, "dimension"),
194
+ (Collection, MINIMAL_COLLECTION, "collection"),
195
+ ],
196
+ )
197
+ def test_class_values(self, model_class, input_data, expected):
198
+ """Test that class values are validated correctly."""
199
+ model = model_class.model_validate(input_data)
200
+ assert model.class_ == expected
201
+
202
+ def test_invalid_class_value(self):
203
+ """Test that an invalid class value fails validation."""
204
+ data = copy.deepcopy(MINIMAL_DATASET)
205
+ data["class"] = "invalid_class"
206
+ with pytest.raises(ValueError):
207
+ validate_jsonstat(data)
208
+
209
+
210
+ class TestComplexStructures:
211
+ """Test cases for complex JSON-stat structures."""
212
+
213
+ def test_nested_collections(self):
214
+ """Test that nested collections validate successfully."""
215
+ # A collection containing a dataset and another collection
216
+ collection = {
217
+ "version": "2.0",
218
+ "class": "collection",
219
+ "link": {
220
+ "item": [
221
+ {
222
+ "class": "dataset",
223
+ "href": "https://json-stat.org/samples/oecd.json",
224
+ },
225
+ {
226
+ "class": "collection",
227
+ "link": {
228
+ "item": [
229
+ {
230
+ "class": "dataset",
231
+ "href": "https://json-stat.org/samples/canada.json",
232
+ }
233
+ ]
234
+ },
235
+ },
236
+ ]
237
+ },
238
+ }
239
+ assert validate_jsonstat(collection) is True
240
+
241
+ def test_multi_level_category_hierarchy(self):
242
+ """Test that a multi-level category hierarchy validates successfully."""
243
+ dimension = copy.deepcopy(MINIMAL_DIMENSION)
244
+ dimension["category"]["index"] = ["total", "a", "a1", "a2", "b", "b1", "b2"]
245
+ dimension["category"]["child"] = {
246
+ "total": ["a", "b"],
247
+ "a": ["a1", "a2"],
248
+ "b": ["b1", "b2"],
249
+ }
250
+ assert validate_jsonstat(dimension) is True
251
+
252
+ def test_mixed_value_status_formats(self):
253
+ """Test that a dataset with mixed value and status formats validates."""
254
+ data = copy.deepcopy(MINIMAL_DATASET)
255
+ data["value"] = {
256
+ "0:0": 1,
257
+ "0:1": 2,
258
+ "0:2": 3,
259
+ "1:0": 4,
260
+ "1:1": 5,
261
+ "1:2": 6,
262
+ }
263
+ data["status"] = {"0:0": "a", "1:1": "b"}
264
+ assert validate_jsonstat(data) is True
265
+
266
+
267
+ class TestEdgeCases:
268
+ """Test cases for edge cases in JSON-stat objects."""
269
+
270
+ def test_empty_dataset_values(self):
271
+ """Test that a dataset with empty values validates successfully."""
272
+ data = copy.deepcopy(MINIMAL_DATASET)
273
+ data["value"] = []
274
+ data["size"] = []
275
+ data["id"] = []
276
+ data["dimension"] = {}
277
+ assert validate_jsonstat(data) is True
278
+
279
+ def test_single_category_dimension(self):
280
+ """Test that a dimension with a single category validates without index."""
281
+ dimension = {
282
+ "version": "2.0",
283
+ "class": "dimension",
284
+ "category": {"label": {"a": "A"}},
285
+ }
286
+ assert validate_jsonstat(dimension) is True
287
+
288
+ def test_iso8601_datetime_format(self):
289
+ """Test that ISO 8601 datetime formats are validated correctly."""
290
+ data = copy.deepcopy(MINIMAL_DATASET)
291
+ # Valid formats
292
+ data["updated"] = "2022-01-01T12:30:45Z"
293
+ assert validate_jsonstat(data) is True
294
+ data["updated"] = "2022-01-01T12:30:45+01:00"
295
+ assert validate_jsonstat(data) is True
296
+ # Invalid format
297
+ data["updated"] = "01/01/2022"
298
+ with pytest.raises(ValueError):
299
+ validate_jsonstat(data)
300
+
301
+ def test_extension_fields(self):
302
+ """Test that extension fields are allowed in JSON-stat objects."""
303
+ data = copy.deepcopy(MINIMAL_DATASET)
304
+ data["extension"] = {"custom_field": "custom_value"}
305
+ assert validate_jsonstat(data) is True
306
+
307
+ def test_mixed_value_types(self):
308
+ """Test that datasets with mixed value types validate successfully."""
309
+ data = copy.deepcopy(MINIMAL_DATASET)
310
+ data["value"] = [1, "2", 3.0, None, 5, 6]
311
+ assert validate_jsonstat(data) is True
312
+
313
+ def test_unit_position_validation(self):
314
+ """Test that unit position is validated correctly."""
315
+ dimension = {
316
+ "version": "2.0",
317
+ "class": "dimension",
318
+ "category": {
319
+ "index": ["gdp", "pop"],
320
+ "unit": {
321
+ "gdp": {
322
+ "decimals": 1,
323
+ "symbol": "$",
324
+ "position": "start",
325
+ },
326
+ "pop": {
327
+ "decimals": 0,
328
+ "symbol": "people",
329
+ "position": "end",
330
+ },
331
+ },
332
+ },
333
+ }
334
+ assert validate_jsonstat(dimension) is True
335
+
336
+ # Invalid position value
337
+ dimension["category"]["unit"]["gdp"]["position"] = "invalid"
338
+ with pytest.raises(ValueError):
339
+ validate_jsonstat(dimension)
340
+
341
+ def test_coordinates_validation(self):
342
+ """Test that coordinates are validated correctly."""
343
+ dimension = {
344
+ "version": "2.0",
345
+ "class": "dimension",
346
+ "category": {
347
+ "index": ["US", "CA", "MX"],
348
+ "coordinates": {
349
+ "US": [-98.5795, 39.8282],
350
+ "CA": [-106.3468, 56.1304],
351
+ "MX": [-102.5528, 23.6345],
352
+ },
353
+ },
354
+ }
355
+ assert validate_jsonstat(dimension) is True
356
+ # Invalid coordinates format
357
+ dimension["category"]["coordinates"]["US"] = [-98.5795] # Missing latitude
358
+ with pytest.raises(ValueError):
359
+ validate_jsonstat(dimension)
360
+
361
+ def test_child_parent_validation(self):
362
+ """Test that child references to parent categories are validated."""
363
+ dimension = {
364
+ "version": "2.0",
365
+ "class": "dimension",
366
+ "category": {
367
+ "index": ["total", "a", "b"],
368
+ "child": {"total": ["a", "b"]},
369
+ },
370
+ }
371
+ assert validate_jsonstat(dimension) is True
372
+ # Invalid parent reference
373
+ dimension["category"]["child"] = {"invalid_parent": ["a", "b"]}
374
+ with pytest.raises(ValueError):
375
+ validate_jsonstat(dimension)
376
+
377
+ def test_unit_category_validation(self):
378
+ """Test that unit references to categories are validated."""
379
+ dimension = {
380
+ "version": "2.0",
381
+ "class": "dimension",
382
+ "category": {
383
+ "index": ["gdp", "pop"],
384
+ "unit": {
385
+ "gdp": {"decimals": 1},
386
+ "pop": {"decimals": 0},
387
+ },
388
+ },
389
+ }
390
+ assert validate_jsonstat(dimension) is True
391
+ # Invalid category reference
392
+ dimension["category"]["unit"] = {"invalid_category": {"decimals": 1}}
393
+ with pytest.raises(ValueError):
394
+ validate_jsonstat(dimension)
395
+
396
+ def test_multi_role_validation(self):
397
+ """Test that dimensions referenced in multiple roles are validated."""
398
+ data = copy.deepcopy(MINIMAL_DATASET)
399
+ # Valid: different dimensions in different roles
400
+ data["role"] = {
401
+ "time": ["time"],
402
+ "geo": ["geo"],
403
+ }
404
+ assert validate_jsonstat(data) is True
405
+ # Invalid: same dimension in multiple roles
406
+ data["role"] = {
407
+ "time": ["time", "geo"],
408
+ "geo": ["geo"],
409
+ }
410
+ with pytest.raises(ValueError):
411
+ validate_jsonstat(data)
@@ -0,0 +1,16 @@
1
+ {
2
+ "version" : "2.0",
3
+ "class" : "collection",
4
+ "href" : "http://json-stat.org/samples/areas/canada.json",
5
+ "label" : "Area: Canada",
6
+ "updated" : "2016-01-01",
7
+ "link" : {
8
+ "item" : [
9
+ {
10
+ "class" : "dataset",
11
+ "href" : "http://json-stat.org/samples/canada.json",
12
+ "label" : "Population by sex and age group. Canada. 2012"
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "version" : "2.0",
3
+ "class" : "collection",
4
+ "href" : "http://json-stat.org/samples/areas/galicia.json",
5
+ "label" : "Area: Galicia",
6
+ "updated" : "2016-01-01",
7
+ "link" : {
8
+ "item" : [
9
+ {
10
+ "class" : "dataset",
11
+ "href" : "http://json-stat.org/samples/galicia.json",
12
+ "label" : "Population by province of residence, place of birth, age, gender and year in Galicia"
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "version" : "2.0",
3
+ "class" : "collection",
4
+ "href" : "http://json-stat.org/samples/areas/index.json",
5
+ "label" : "Areas",
6
+ "updated" : "2016-01-01",
7
+ "link" : {
8
+ "item" : [
9
+ {
10
+ "class" : "collection",
11
+ "href" : "http://json-stat.org/samples/areas/us.json",
12
+ "label" : "Area: United States"
13
+ },
14
+ {
15
+ "class" : "collection",
16
+ "href" : "http://json-stat.org/samples/areas/canada.json",
17
+ "label" : "Area: Canada"
18
+ },
19
+ {
20
+ "class" : "collection",
21
+ "href" : "http://json-stat.org/samples/areas/galicia.json",
22
+ "label" : "Area: Galicia"
23
+ },
24
+ {
25
+ "class" : "collection",
26
+ "href" : "http://json-stat.org/samples/areas/oecd.json",
27
+ "label" : "Area: OECD Zone"
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "version" : "2.0",
3
+ "class" : "collection",
4
+ "href" : "http://json-stat.org/samples/areas/oecd.json",
5
+ "label" : "Area: OECD Zone",
6
+ "updated" : "2016-01-01",
7
+ "link" : {
8
+ "item" : [
9
+ {
10
+ "class" : "dataset",
11
+ "href" : "http://json-stat.org/samples/oecd.json",
12
+ "label" : "Unemployment rate in the OECD countries 2003-2014"
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "version" : "2.0",
3
+ "class" : "collection",
4
+ "href" : "http://json-stat.org/samples/areas/us.json",
5
+ "label" : "Area: United States",
6
+ "updated" : "2016-01-01",
7
+ "link" : {
8
+ "item" : [
9
+ {
10
+ "class" : "dataset",
11
+ "href" : "http://json-stat.org/samples/us-gsp.json",
12
+ "label" : "US States by GSP and population"
13
+ },
14
+ {
15
+ "class" : "dataset",
16
+ "href" : "http://json-stat.org/samples/us-unr.json",
17
+ "label" : "Unemployment Rates by County, 2012 Annual Averages"
18
+ },
19
+ {
20
+ "class" : "dataset",
21
+ "href" : "http://json-stat.org/samples/us-labor.json",
22
+ "label" : "Labor Force Data by County, 2012 Annual Averages"
23
+ }
24
+ ]
25
+ }
26
+ }