context
-------

JsonValue loading:

* accepts any context for input.

* convert input to easy to use Python values.

JsonValue is for conversion, not validation. It rejects bad input --
input where the types don't match the values, or where types cannot be
interpreted. It doesn't do validation. Validation should be done using
JSON-Schema, or expand and explicit type check, or go to Python
objects and validate them, or a combination of such.

Validation strategy
-------------------

* you can specify per type what terms it needs. writable and
  readable. If writable term is missing, then validation error. What
  then about not-required values?

* a type can also specify it wants a single value of something (not a
  multiple), or that it requires a multiple (in which case you'll get
  a list or set).

* what about node types of sub-nodes?

* A request method sets what type (or Python class) it wants.

document loader
---------------

* dealing with loading docs from URLs.

extra parameters
----------------

* We need to make sure keyword parameters (such as request) are passed
  along to load_value/dump_value.

docs
----

* API docs.

  * describe exceptions that can happen in docs.

errors
------

* do we need specific errors for loading and dumping nodes too,
  instead of just for values? How to deal with error handling here?
  Always fail if load/dump function errors? I think so, but allow the
  user to raise a custom NodeLoadError to signal a problem in the
  data.

* what is the right behavior for a value type that we don't have a
  loader/dumper for? For the loader I now have a mode where I can
  reject unknown/missing types. For the dumper it's less important as
  we control the context, but it might be handy for debugging.

* should we have an load error where a value described in the context
  is required, i.e. if it is missing/None then it's an error?

* should we reject loading/dumping nodes (as opposed to values) where
  we don't recognize the node type?

morepath integration
--------------------

Some speculative API.

First the minimum loosely coupled API to make an application do this
at all::


  @App.load_json()
  def transform_request_json(json, request):
      # can look at any property of json whatsoever to create any instance
      # whatsoever
      # the default is a no-op, i.e. return json

  @App.dump_json():
  def transform_response_json(obj, request):
      # can do whatever it wants with obj and request to do the
      # transformation, returning JSON-serializable structure
      # the default is json.dumps (which will give an error if it cannot
      # dump things at all)

  # json views allow a model_body argument. This is the result of
  # load_json. it's also stored on request.body.
  @App.json(model=Collection, request_method=POST, model_body=Item)
  def collection_add(self, body):
      # body is going to be the value in request.body. request is
      # optional, meaning we use mapply.

      # we can return any instance that dump_json understands
      return self

  # if we want to add a context to the json before it comes in, we can hook in
  # custom per-method transform
  @App.json(model=Collection, request_method=POST,
            request_transform=context_add('http://example.com/context'))
  def collection_add(self, request):
      ...

  We can similarly hook in a response_transform (or is this
  render?). Less needed.

Now to plug in JsonValue::

  # set the json_value converter that applies in Morepath
  @App.json_value()
  def get_json_value():
      return json_value

  # we can now plug in a custom tranform of request and response

  @App.load_json()
  def transform_request_json(json, request):
      jv = json_value(lookup=request.lookup)
      return jv.load_objects(json, request=request)

  @App.dump_json()
  def dump_response_json(obj, request):
      jv = json_value(lookup=request.lookup)
      # XXX dump context?
      return jv.dump_objects(obj, request=request)

  # calls decorated to get type. decorated may be class
  # or function that returns function with dump and load methods,
  # and validate_load and validate_dump.
  @App.value_type(iri='http://example.com/date')
  def get_value_type():
      pass

  @App.node_type(iri='http://example.com/foo', model=Foo, context=load_context)
  def get_node_type():
      pass

  # XXX how to load a whole vocabulary such as schema.org? Don't want
  # to define it twice.
  @App.value_vocab()
  def get_value_vocab():
     return {

     }

  # XXX what's missing is a way to specify dump context. per object or
  # general or both?


  # somehow recognize application/ld-json in POST/PUT. Automatically
  # expand it using its supplied context. expansion may fail which is
  # a 400 error. Result is put on request, request.json_expanded.



