Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coercing parse #7

Open
Zulu-Inuoe opened this issue Jan 18, 2021 · 2 comments
Open

coercing parse #7

Zulu-Inuoe opened this issue Jan 18, 2021 · 2 comments
Labels
enhancement New feature or request
Milestone

Comments

@Zulu-Inuoe
Copy link
Owner

Zulu-Inuoe commented Jan 18, 2021

A common feature in JSON parsers is automatic object mapping into language data types such as structures, classes, etc.

While not everyone (myself included) likes automatic conversions like this, it is without question incredibly useful & convenient when developing & doing exploratory programming.

For example, consider the following:

(defpackage #:com.inuoe.jzon.coerce-example
  (:use #:cl)
  (:local-nicknames
   (#:jzon #:com.inuoe.jzon)))

(in-package #:com.inuoe.jzon.coerce-example)

(defclass coordinate ()
  ((reference
    :initarg :reference)
   (x
    :initform 0
    :initarg :x
    :accessor x)
   (y
    :initform 0
    :initarg :y
    :accessor y)))

(defclass object ()
  ((alive
    :initform nil
    :initarg :alive
    :type boolean)
   (coordinate
    :initform nil
    :initarg :coordinate
    :type (or null coordinate))
   (children
    :initform nil
    :initarg :children
    :type list)))

(let ((obj (jzon:parse (jzon:stringify (make-instance 'object :coordinate (make-instance 'coordinate))) :type 'object)))
  (with-slots (alive coordinate children) obj
    (print alive) ; nil
    (print coordinate) ; #<coordinate ...>
    (print children) ; nil

    (with-slots (x y) coordinate
      (print (slot-boundp coordinate 'reference)) ; nil
      (print x) ; 0
      (print y) ; 0
      )))

An alternative approach is to offer object coercion post-parse on the returned hash-table. And yet a third approach is to provide both options: allow a :type option to parse, but also export a coerce-value or equivalent function that users can use on say, a hash table.

Given the following JSON

{  
   "authenticationResultCode":"ValidCredentials",  
   "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png",  
   "copyright":"Copyright © 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",  
   "resourceSets":[  
      {  
         "estimatedTotal":1,  
         "resources":[  
            {  
               "__type":"ElevationData:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1",  
               "elevations":[1776,1775,1777,1776],  
               "zoomLevel":14  
            }  
         ]  
      }  
   ],  
   "statusCode":200,  
   "statusDescription":"OK",  
   "traceId":"8d57dbeb0bb94e7ca67fd25b4114f5c3"  
}

from Bing Maps API

This allows the following:

(defclass elevation-data ()
  ((elevations
    :type (vector integer))
   (|zoomLevel|
    :type integer)))

(let* ((data (json:parse "..."))
       (data (gethash "resourceSets" data))
       (data (gethash "resources" data))
       (data (aref data 0))
       (obj (jzon:coerce-into data 'elevation-data)))
  (format t "Zoom level is ~D~%" (slot-value obj '|zoomLevel|)))

where we don't need to coerce parse the full object hierarchy, just the deeply nested elevation-data object

@Zulu-Inuoe Zulu-Inuoe added the enhancement New feature or request label Jan 18, 2021
@Zulu-Inuoe Zulu-Inuoe added this to the v1 & Publish to Quicklisp milestone Jan 18, 2021
@Valera
Copy link

Valera commented Nov 30, 2021

In my experience with REST APIs, often there is "Validate and Serialize" step in JSON request processing,
and it usually is separated from JSON parsing. This usually requires creating an additional object,
usually descendant of some Serializer class.

Your approach is to construct object from json using information about class slot definition.
This is very handy but this can possibly skip validation step and cause some unwanted code execution,
for example allocation of an array of huge size in object's constructor.

Also approach with serializer as a separate object can do more smart conversions to user-defined types.
And it is more robust when you are changing object implementation.

In my opinion, getting validation and serialization right is a difficult topic, personally I would suggest
releasing v1 without any support for coercion.

I will also refresh my knowledge of validator and serializer libraries in CL and write here my thoughts about them.
Maybe jzon's readme can just recommend some other library.

@Zulu-Inuoe
Copy link
Owner Author

Thank you for the feedback, it's really appreciated.

As somebody who uses C# regularly, I've become very attached/biased to how Newtonsoft JSON in particular goes about doing things, and ultimately with jzon I am looking to replicate the same general decisions made there.

While I understand the dangers of doing all sorts of automagic coercion, it is as you said, very handy and applicable for many purposes when you "own" the incoming JSON or can otherwise trust it. Couple that with a JSON schema and you get a good enough safety for most uses where you don't get sent malicious JSON.

That all said, you may be right for a v1 without it but my only trouble with that is: Am I offering enough different from all the other JSON libraries in CL right now? Because I don't really think so.
So I feel I need this and/or #3 - which is essentially an implementation of JObject from Newtonsoft JSON - implemented before I can feel comfortable having this out in public.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

2 participants