f0020e905c94

Fix slot map/alist initialization

Instead of doing this on `finalize-inheritance`, do it after `compute-slots`.
I swear, sometimes using the MOP feels like playing whack-a-mole.

Also adds another real-world test case.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Mon, 31 Aug 2020 22:18:48 -0400
parents 700d5c649245
children f91e6bc7fd56
branches/tags (none)
files src/mop.lisp test/data/openweathermap/onecall.json test/tests.lisp

Changes

--- a/src/mop.lisp	Sun Aug 30 00:22:27 2020 -0400
+++ b/src/mop.lisp	Mon Aug 31 22:18:48 2020 -0400
@@ -38,7 +38,9 @@
    (before-print :initarg :json/before-print :accessor before-print :initform nil)
    (after-read :initarg :json/after-read :accessor after-read :initform nil)))
 
-(defmethod make-instance ((class (eql (find-class 'json-effective-slot-definition))) &rest initargs)
+(defmethod make-instance ((class (eql (find-class 'json-effective-slot-definition)))
+                          &rest args
+                          &key initargs name &allow-other-keys)
   ;; We need to wrap this to make sure that every JSON slot has at least one
   ;; initarg, so we can use it when calling `make-instance` during reading.
   ;;
@@ -46,10 +48,10 @@
   ;; we can use an existing one and not clutter things up.
   (apply #'call-next-method class
          (append
-           (when (null (getf initargs :initargs))
+           (when (null initargs)
              (list :initargs
-                   (list (gensym (string (getf initargs :name))))))
-           initargs)))
+                   (list (gensym (string name)))))
+           args)))
 
 
 (defmethod c2mop:direct-slot-definition-class ((class json-class) &rest initargs)
@@ -66,6 +68,7 @@
   ;; like it does in direct-slot-definition-class.  So we need another way to
   ;; know which class to use here.
   (or *effective-slot-definition-class* (call-next-method)))
+
 (defmethod c2mop:compute-effective-slot-definition ((class json-class) name direct-slots)
   (if (not (some (lambda (dslot)
                    (typep dslot 'json-direct-slot-definition))
@@ -91,18 +94,18 @@
       eslot)))
 
 
-(defun json-slots (class)
+(defun json-slots (effective-slots)
   (remove-if-not (lambda (slot) (typep slot 'json-effective-slot-definition))
-                 (c2mop:class-slots class)))
+                 effective-slots))
 
-(defun make-slot-map (class)
-  "Return a slot map for the JSON slots of `class`, used when reading.
+(defun make-slot-map (effective-slots)
+  "Return a slot map for the JSON slots in `effective-slots`, used when reading.
 
   The result will be a hash table of `{name: (initarg class contained-class
   after-read)}`.
 
   "
-  (let* ((slots (json-slots class))
+  (let* ((slots (json-slots effective-slots))
          (result (make-hash-table :test #'equal :size (length slots))))
     (dolist (slot slots)
       (destructuring-bind (c &optional cc) (json-class slot)
@@ -110,8 +113,8 @@
               (list (json-initarg slot) c cc (after-read slot)))))
     result))
 
-(defun make-slot-alist (class)
-  "Return a slot alist for the JSON slots of `class`, used when printing.
+(defun make-slot-alist (effective-slots)
+  "Return a slot alist for the JSON slots of `effective-slots`, used when printing.
 
   The result will be an alist of `((slot . (\"name\" before-print)))`.
 
@@ -120,7 +123,14 @@
             (cons (c2mop:slot-definition-name slot)
                   (list (json-name slot)
                         (before-print slot))))
-          (json-slots class)))
+          (json-slots effective-slots)))
+
+
+(defmethod c2mop:compute-slots :around ((class json-class))
+  (let ((effective-slots (call-next-method)))
+    (setf (slot-map class) (make-slot-map effective-slots)
+          (slot-alist class) (make-slot-alist effective-slots))
+    effective-slots))
 
 
 (defmethod shared-initialize :around
@@ -173,11 +183,6 @@
     (call-next-method)))
 
 
-(defmethod c2mop:finalize-inheritance :after ((class json-class))
-  (setf (slot-map class) (make-slot-map class)
-        (slot-alist class) (make-slot-alist class)))
-
-
 ;;;; Read ---------------------------------------------------------------------
 (defun parse-json-class (class-name class input)
   (unless (allow-read class)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/openweathermap/onecall.json	Mon Aug 31 22:18:48 2020 -0400
@@ -0,0 +1,1 @@
+{"lat":43.16,"lon":-77.61,"timezone":"America/New_York","timezone_offset":-14400,"current":{"dt":1598923388,"sunrise":1598870060,"sunset":1598917638,"temp":73.06,"feels_like":69.28,"pressure":1014,"humidity":56,"dew_point":56.44,"uvi":6.94,"clouds":75,"visibility":10000,"wind_speed":10.29,"wind_deg":180,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}]},"minutely":[{"dt":1598923440,"precipitation":0},{"dt":1598923500,"precipitation":0},{"dt":1598923560,"precipitation":0},{"dt":1598923620,"precipitation":0},{"dt":1598923680,"precipitation":0},{"dt":1598923740,"precipitation":0},{"dt":1598923800,"precipitation":0},{"dt":1598923860,"precipitation":0},{"dt":1598923920,"precipitation":0},{"dt":1598923980,"precipitation":0},{"dt":1598924040,"precipitation":0},{"dt":1598924100,"precipitation":0},{"dt":1598924160,"precipitation":0},{"dt":1598924220,"precipitation":0},{"dt":1598924280,"precipitation":0},{"dt":1598924340,"precipitation":0},{"dt":1598924400,"precipitation":0},{"dt":1598924460,"precipitation":0},{"dt":1598924520,"precipitation":0},{"dt":1598924580,"precipitation":0},{"dt":1598924640,"precipitation":0},{"dt":1598924700,"precipitation":0},{"dt":1598924760,"precipitation":0},{"dt":1598924820,"precipitation":0},{"dt":1598924880,"precipitation":0},{"dt":1598924940,"precipitation":0},{"dt":1598925000,"precipitation":0},{"dt":1598925060,"precipitation":0},{"dt":1598925120,"precipitation":0},{"dt":1598925180,"precipitation":0},{"dt":1598925240,"precipitation":0},{"dt":1598925300,"precipitation":0},{"dt":1598925360,"precipitation":0},{"dt":1598925420,"precipitation":0},{"dt":1598925480,"precipitation":0},{"dt":1598925540,"precipitation":0},{"dt":1598925600,"precipitation":0},{"dt":1598925660,"precipitation":0},{"dt":1598925720,"precipitation":0},{"dt":1598925780,"precipitation":0},{"dt":1598925840,"precipitation":0},{"dt":1598925900,"precipitation":0},{"dt":1598925960,"precipitation":0},{"dt":1598926020,"precipitation":0},{"dt":1598926080,"precipitation":0},{"dt":1598926140,"precipitation":0},{"dt":1598926200,"precipitation":0},{"dt":1598926260,"precipitation":0},{"dt":1598926320,"precipitation":0},{"dt":1598926380,"precipitation":0},{"dt":1598926440,"precipitation":0},{"dt":1598926500,"precipitation":0},{"dt":1598926560,"precipitation":0},{"dt":1598926620,"precipitation":0},{"dt":1598926680,"precipitation":0},{"dt":1598926740,"precipitation":0},{"dt":1598926800,"precipitation":0},{"dt":1598926860,"precipitation":0},{"dt":1598926920,"precipitation":0},{"dt":1598926980,"precipitation":0},{"dt":1598927040,"precipitation":0}],"hourly":[{"dt":1598922000,"temp":73.06,"feels_like":67.73,"pressure":1014,"humidity":56,"dew_point":56.44,"clouds":75,"visibility":10000,"wind_speed":13.02,"wind_deg":162,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1598925600,"temp":70.66,"feels_like":66.69,"pressure":1014,"humidity":69,"dew_point":60.01,"clouds":51,"visibility":10000,"wind_speed":12.86,"wind_deg":172,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1598929200,"temp":69.37,"feels_like":66.43,"pressure":1014,"humidity":75,"dew_point":61.11,"clouds":46,"visibility":10000,"wind_speed":11.77,"wind_deg":175,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1598932800,"temp":68.49,"feels_like":65.43,"pressure":1014,"humidity":78,"dew_point":61.36,"clouds":44,"visibility":10000,"wind_speed":12.17,"wind_deg":186,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1598936400,"temp":68.63,"feels_like":65.1,"pressure":1014,"humidity":76,"dew_point":60.76,"clouds":50,"visibility":10000,"wind_speed":12.57,"wind_deg":185,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1598940000,"temp":68,"feels_like":65.21,"pressure":1015,"humidity":77,"dew_point":60.85,"clouds":49,"visibility":10000,"wind_speed":11.12,"wind_deg":176,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1598943600,"temp":67.12,"feels_like":64.87,"pressure":1015,"humidity":79,"dew_point":60.69,"clouds":66,"visibility":10000,"wind_speed":10.07,"wind_deg":174,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1598947200,"temp":67.23,"feels_like":64.85,"pressure":1015,"humidity":79,"dew_point":60.66,"clouds":81,"visibility":10000,"wind_speed":10.36,"wind_deg":177,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1598950800,"temp":66.78,"feels_like":64.4,"pressure":1015,"humidity":80,"dew_point":60.48,"clouds":87,"visibility":10000,"wind_speed":10.29,"wind_deg":178,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1598954400,"temp":65.77,"feels_like":63.59,"pressure":1015,"humidity":83,"dew_point":60.55,"clouds":80,"visibility":10000,"wind_speed":9.98,"wind_deg":183,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0.01},{"dt":1598958000,"temp":65.55,"feels_like":62.73,"pressure":1015,"humidity":84,"dew_point":60.67,"clouds":72,"visibility":10000,"wind_speed":11.21,"wind_deg":174,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.01},{"dt":1598961600,"temp":67.12,"feels_like":64.31,"pressure":1016,"humidity":81,"dew_point":61.2,"clouds":60,"visibility":10000,"wind_speed":11.52,"wind_deg":184,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.01},{"dt":1598965200,"temp":69.76,"feels_like":66.49,"pressure":1016,"humidity":75,"dew_point":61.83,"clouds":1,"visibility":10000,"wind_speed":12.64,"wind_deg":194,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1598968800,"temp":72.34,"feels_like":70.29,"pressure":1016,"humidity":72,"dew_point":63.05,"clouds":8,"visibility":10000,"wind_speed":11.39,"wind_deg":194,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1598972400,"temp":74.95,"feels_like":74.07,"pressure":1016,"humidity":69,"dew_point":64.47,"clouds":23,"visibility":10000,"wind_speed":10.25,"wind_deg":198,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1598976000,"temp":77.07,"feels_like":77.23,"pressure":1016,"humidity":67,"dew_point":65.55,"clouds":33,"visibility":10000,"wind_speed":9.31,"wind_deg":202,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0},{"dt":1598979600,"temp":77.85,"feels_like":78.48,"pressure":1015,"humidity":67,"dew_point":66.25,"clouds":42,"visibility":10000,"wind_speed":9.06,"wind_deg":202,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0},{"dt":1598983200,"temp":78.44,"feels_like":78.67,"pressure":1015,"humidity":66,"dew_point":66.34,"clouds":51,"visibility":10000,"wind_speed":9.84,"wind_deg":202,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.26,"rain":{"1h":0.12}},{"dt":1598986800,"temp":80.37,"feels_like":80.08,"pressure":1014,"humidity":61,"dew_point":66.13,"clouds":31,"visibility":10000,"wind_speed":10.42,"wind_deg":207,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.22},{"dt":1598990400,"temp":80.73,"feels_like":81.32,"pressure":1014,"humidity":62,"dew_point":66.56,"clouds":29,"visibility":10000,"wind_speed":9.48,"wind_deg":203,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.24},{"dt":1598994000,"temp":79.84,"feels_like":79.95,"pressure":1013,"humidity":65,"dew_point":67.44,"clouds":42,"visibility":10000,"wind_speed":10.78,"wind_deg":202,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.32,"rain":{"1h":0.18}},{"dt":1598997600,"temp":77.67,"feels_like":77.68,"pressure":1013,"humidity":70,"dew_point":67.46,"clouds":56,"visibility":10000,"wind_speed":11.01,"wind_deg":196,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.4,"rain":{"1h":0.11}},{"dt":1599001200,"temp":75.34,"feels_like":75.11,"pressure":1014,"humidity":74,"dew_point":66.6,"clouds":61,"visibility":10000,"wind_speed":10.96,"wind_deg":183,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.34},{"dt":1599004800,"temp":72.7,"feels_like":71.69,"pressure":1014,"humidity":77,"dew_point":65.23,"clouds":66,"visibility":10000,"wind_speed":11.23,"wind_deg":178,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0.28},{"dt":1599008400,"temp":71.47,"feels_like":69.73,"pressure":1014,"humidity":78,"dew_point":64.51,"clouds":97,"visibility":10000,"wind_speed":11.92,"wind_deg":179,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.11},{"dt":1599012000,"temp":71.02,"feels_like":68.97,"pressure":1014,"humidity":79,"dew_point":64.35,"clouds":98,"visibility":10000,"wind_speed":12.39,"wind_deg":182,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.08},{"dt":1599015600,"temp":70.95,"feels_like":68.67,"pressure":1013,"humidity":80,"dew_point":64.51,"clouds":99,"visibility":10000,"wind_speed":13.04,"wind_deg":183,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.12},{"dt":1599019200,"temp":70.77,"feels_like":67.89,"pressure":1013,"humidity":81,"dew_point":64.85,"clouds":99,"visibility":10000,"wind_speed":14.23,"wind_deg":190,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":0.3,"rain":{"1h":0.16}},{"dt":1599022800,"temp":70.77,"feels_like":68.52,"pressure":1012,"humidity":82,"dew_point":65.26,"clouds":99,"visibility":10000,"wind_speed":13.38,"wind_deg":185,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":0.46,"rain":{"1h":0.77}},{"dt":1599026400,"temp":71.46,"feels_like":69.71,"pressure":1012,"humidity":81,"dew_point":65.53,"clouds":99,"visibility":10000,"wind_speed":12.73,"wind_deg":186,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":0.48,"rain":{"1h":0.68}},{"dt":1599030000,"temp":72.19,"feels_like":70.59,"pressure":1011,"humidity":79,"dew_point":65.59,"clouds":100,"visibility":10000,"wind_speed":12.46,"wind_deg":186,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":0.66,"rain":{"1h":0.43}},{"dt":1599033600,"temp":71.62,"feels_like":70.2,"pressure":1010,"humidity":82,"dew_point":66.15,"clouds":100,"visibility":10000,"wind_speed":12.55,"wind_deg":187,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":0.98,"rain":{"1h":0.82}},{"dt":1599037200,"temp":71.13,"feels_like":69.87,"pressure":1010,"humidity":85,"dew_point":66.54,"clouds":100,"visibility":10000,"wind_speed":12.71,"wind_deg":197,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10n"}],"pop":0.98,"rain":{"1h":1.61}},{"dt":1599040800,"temp":70.63,"feels_like":70.77,"pressure":1010,"humidity":88,"dew_point":66.92,"clouds":100,"visibility":10000,"wind_speed":10.63,"wind_deg":198,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10n"}],"pop":0.99,"rain":{"1h":1.39}},{"dt":1599044400,"temp":69.8,"feels_like":69.26,"pressure":1009,"humidity":91,"dew_point":67.23,"clouds":100,"visibility":10000,"wind_speed":11.99,"wind_deg":199,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"pop":1,"rain":{"1h":1.29}},{"dt":1599048000,"temp":70.02,"feels_like":69.78,"pressure":1009,"humidity":89,"dew_point":66.9,"clouds":100,"visibility":10000,"wind_speed":11.07,"wind_deg":180,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.8},{"dt":1599051600,"temp":71.38,"feels_like":70.07,"pressure":1009,"humidity":85,"dew_point":66.9,"clouds":83,"visibility":10000,"wind_speed":13.02,"wind_deg":191,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1599055200,"temp":75.43,"feels_like":73.08,"pressure":1008,"humidity":76,"dew_point":67.55,"clouds":48,"visibility":10000,"wind_speed":15.43,"wind_deg":200,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.05},{"dt":1599058800,"temp":79.03,"feels_like":76.15,"pressure":1008,"humidity":65,"dew_point":66.69,"clouds":34,"visibility":10000,"wind_speed":15.48,"wind_deg":213,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.08},{"dt":1599062400,"temp":81.57,"feels_like":79.14,"pressure":1007,"humidity":59,"dew_point":65.98,"clouds":36,"visibility":10000,"wind_speed":14.34,"wind_deg":220,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.08},{"dt":1599066000,"temp":82.65,"feels_like":80.29,"pressure":1007,"humidity":58,"dew_point":66.58,"clouds":31,"visibility":10000,"wind_speed":14.65,"wind_deg":229,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0.17},{"dt":1599069600,"temp":81.97,"feels_like":80.15,"pressure":1007,"humidity":62,"dew_point":68.02,"clouds":31,"visibility":10000,"wind_speed":14.74,"wind_deg":237,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.25,"rain":{"1h":0.28}},{"dt":1599073200,"temp":80.96,"feels_like":79.68,"pressure":1007,"humidity":67,"dew_point":69.01,"clouds":25,"visibility":10000,"wind_speed":14.9,"wind_deg":240,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.89,"rain":{"1h":0.44}},{"dt":1599076800,"temp":80.76,"feels_like":79.79,"pressure":1007,"humidity":67,"dew_point":69.12,"clouds":41,"visibility":10000,"wind_speed":14.18,"wind_deg":242,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.98,"rain":{"1h":0.28}},{"dt":1599080400,"temp":79.03,"feels_like":78.87,"pressure":1007,"humidity":73,"dew_point":69.96,"clouds":52,"visibility":10000,"wind_speed":13.49,"wind_deg":253,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.98,"rain":{"1h":0.7}},{"dt":1599084000,"temp":76.78,"feels_like":77.54,"pressure":1007,"humidity":80,"dew_point":70.23,"clouds":63,"visibility":10000,"wind_speed":12.33,"wind_deg":268,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":1,"rain":{"1h":0.71}},{"dt":1599087600,"temp":74.55,"feels_like":75.74,"pressure":1008,"humidity":84,"dew_point":69.51,"clouds":55,"visibility":10000,"wind_speed":10.92,"wind_deg":267,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":1,"rain":{"1h":0.6}},{"dt":1599091200,"temp":72.36,"feels_like":74.41,"pressure":1008,"humidity":87,"dew_point":68.47,"clouds":49,"visibility":10000,"wind_speed":8.39,"wind_deg":264,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"pop":1,"rain":{"1h":0.2}}],"daily":[{"dt":1598893200,"sunrise":1598870060,"sunset":1598917638,"temp":{"day":73.06,"min":69.1,"max":73.06,"night":69.1,"eve":73.06,"morn":73.06},"feels_like":{"day":69.78,"night":66.13,"eve":69.78,"morn":69.78},"pressure":1014,"humidity":56,"dew_point":56.44,"wind_speed":9.4,"wind_deg":163,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":75,"pop":0.2,"rain":0.29,"uvi":6.94},{"dt":1598979600,"sunrise":1598956525,"sunset":1599003934,"temp":{"day":78.44,"min":67.17,"max":79.84,"night":71.46,"eve":72.7,"morn":67.17},"feels_like":{"day":78.67,"night":69.71,"eve":71.69,"morn":64.38},"pressure":1015,"humidity":66,"dew_point":66.34,"wind_speed":9.84,"wind_deg":202,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":51,"pop":0.48,"rain":2.29,"uvi":6.92},{"dt":1599066000,"sunrise":1599042991,"sunset":1599090230,"temp":{"day":81.97,"min":65.17,"max":81.97,"night":65.17,"eve":72.36,"morn":70.02},"feels_like":{"day":80.15,"night":66.38,"eve":74.41,"morn":69.78},"pressure":1007,"humidity":62,"dew_point":68.02,"wind_speed":14.74,"wind_deg":237,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":31,"pop":1,"rain":6.18,"uvi":6.7},{"dt":1599152400,"sunrise":1599129456,"sunset":1599176525,"temp":{"day":74.61,"min":65.05,"max":76.06,"night":67.35,"eve":72.5,"morn":65.05},"feels_like":{"day":78.3,"night":60.13,"eve":71.58,"morn":64.87},"pressure":1006,"humidity":83,"dew_point":69.13,"wind_speed":6.2,"wind_deg":220,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":96,"pop":0.86,"rain":4.34,"uvi":6.75},{"dt":1599238800,"sunrise":1599215921,"sunset":1599262819,"temp":{"day":69.39,"min":56.21,"max":69.39,"night":56.21,"eve":63.19,"morn":59.65},"feels_like":{"day":61.74,"night":50.47,"eve":55.96,"morn":54.28},"pressure":1015,"humidity":49,"dew_point":49.75,"wind_speed":13.44,"wind_deg":285,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":9,"pop":0,"uvi":6.65},{"dt":1599325200,"sunrise":1599302386,"sunset":1599349113,"temp":{"day":71.28,"min":56.84,"max":71.28,"night":59.49,"eve":64.62,"morn":56.84},"feels_like":{"day":63.57,"night":54.19,"eve":59.68,"morn":51.12},"pressure":1022,"humidity":45,"dew_point":48.94,"wind_speed":13.27,"wind_deg":282,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":4,"pop":0,"uvi":6.8},{"dt":1599411600,"sunrise":1599388852,"sunset":1599435407,"temp":{"day":80.15,"min":62.19,"max":80.15,"night":70.56,"eve":72.59,"morn":62.19},"feels_like":{"day":74.08,"night":64.72,"eve":65.89,"morn":56.59},"pressure":1017,"humidity":42,"dew_point":55.13,"wind_speed":13.49,"wind_deg":194,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"clouds":22,"pop":0,"uvi":6.96},{"dt":1599498000,"sunrise":1599475317,"sunset":1599521700,"temp":{"day":66.07,"min":54.77,"max":68.52,"night":54.77,"eve":59.29,"morn":68.52},"feels_like":{"day":58.12,"night":51.76,"eve":53.67,"morn":64.42},"pressure":1010,"humidity":60,"dew_point":51.94,"wind_speed":15.14,"wind_deg":288,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":93,"pop":0.96,"rain":9.03,"uvi":6.74}]}
--- a/test/tests.lisp	Sun Aug 30 00:22:27 2020 -0400
+++ b/test/tests.lisp	Mon Aug 31 22:18:48 2020 -0400
@@ -233,6 +233,69 @@
 
 (define-file-test jira/MVNCENTRAL (o "test/data/jira/MVNCENTRAL.json"))
 
+(define-file-test openweathermap/onecall (o "test/data/openweathermap/onecall.json"))
+
+(defclass owm/weather ()
+  ((id :json number)
+   (main :json string)
+   (description :json string)
+   (icon :json string))
+  (:metaclass jarl::json-class))
+
+(defclass owm/day ()
+  ((clouds :json number)
+   (dew-point :json number)
+   (timestamp :json number :json/name "dt")
+   (humidity :json number)
+   (pressure :json number)
+   (sunrise :json number)
+   (sunset :json number)
+   (temperature :json t :json/name "temp")
+   (feels-like :json t)
+   (uvi :json number)
+   (visibility :json number)
+   (weather :json (vector owm/weather))
+   (wind-degree :json number :json/name "wind_deg")
+   (wind-speed :json number))
+  (:metaclass jarl::json-class))
+
+(defclass owm/hour ()
+  ((clouds :json number)
+   (dew-point :json number)
+   (timestamp :json number :json/name "dt")
+   (precipitation :json number :json/name "pop")
+   (humidity :json number)
+   (pressure :json number)
+   (sunset :json number)
+   (temperature :json number :json/name "temp")
+   (feels-like :json number)
+   (visibility :json number)
+   (weather :json (vector owm/weather))
+   (wind-degree :json number :json/name "wind_deg")
+   (wind-speed :json number))
+  (:metaclass jarl::json-class))
+
+(defclass owm/minute ()
+  ((timestamp :json number :json/name "dt")
+   (precipitation :json number))
+  (:metaclass jarl::json-class))
+
+(defclass owm/onecall ()
+  ((latitude :json number :json/name "lat")
+   (longitude :json number :json/name "lon")
+   (timezone :json string)
+   (timezone-offset :json number)
+   (current :json owm/day)
+   (daily :json (vector owm/day))
+   (hourly :json (vector owm/hour))
+   (minutely :json (vector owm/minute)))
+  (:metaclass jarl::json-class))
+
+(define-test parse-owm-data-into-classes
+  ;; At least make sure it doesn't crash.
+  (with-open-file (f "test/data/openweathermap/onecall.json")
+    (is (typep (jarl:read 'owm/onecall f) 'owm/onecall))))
+
 
 ;;;; Error Tests --------------------------------------------------------------
 (defmacro define-error-tests (name &rest clauses)