Writing a JSON Generator
I started working on a service to generate mock json data. A few of these services already exist, but it seemed like it would be a fun thing to write anyway.
The idea is that you POST some model json and a positive integer n to the endpoint and you get back n copies of your object with the fields filled out. For example, the following model
{
"name" : {"first" : "firstName", "last" : "lastName"},
"age" : "personAge",
"email" : "eMail"
}
{
"name" : {"first" : "Sarah", "last" : "MacDonald"},
"age" : "27",
"email" : "smacd@gmail.com"
}
The value strings “firstName” “lastName” and “eMail” in the model are tags. They tell the service what to replace the value with.
This sort of thing is useful for mocking up data-heavy frontends. Instead of making a call to your not-yet-existent backend, just call this endpoint with your model data and the number of records you want.
The core of this service is the function that traverses the model and does the value
replacement. At first I wanted to write the whole service in Go, as it seemed like it would be
a good way to get deeper into the language. However, I quickly realized that doing this
traversal without relying extensively on third-party libraries would be very difficult.
Because of Go’s static typing, one is forced to parse an arbitrary json
object into map[string]interface{}
, and then go through the whole thing doing type checking
and conversions just to access the fields. At least, that seemed to be the only approach I
could find.
So after a few hours of googling and staring at my text editor, I decided to throw in the towel and pull out the python. I didn’t want to do this, as I’m trying to break my habit of doing everything in python. But then I wrote the traversal function and I remembered why I do everything in python:
def replace_values(d):
if isinstance(d, list):
return list(map(replace_values, d))
elif isinstance(d, dict):
return {k: replace_values(v) for k,v in d.items()}
elif isinstance(d, str):
if d in db:
return random.sample(db[d],1)[0]
else:
return None
else:
return None
This function takes the top level json object, whether parsed as a string, list, or dict, and
recursively replaces values with ones randomly sampled from db
, which is a dict that accepts tags
as keys and returns a list of possible values.
Now, this isn’t actually a great example with which to extol the benefits of python. This kind of functional-recursive approach isn’t really a desirable pattern in python, as python doesn’t do tail call elimination. As such, there’s a limit to how deep recursive call stacks may go. In this case, it’s a moot point, since we’re only parsing objects that might be at most several layers deep, and I don’t believe this function could be tail call eliminated anyway. It could probably be rewritten as a while loop, but I think it would likely be more verbose, less readable, and more error prone.
But to get back to the topic at hand, python’s dynamic typing and high-quality json library makes the task fairly trivial, whereas it would have required quite a lot more legwork in Go, with questionable payoff. So while I still want to get deeper into Go, this doesn’t seem to be the project to do it with.
The plan now is to wrap the process with flask, and serve it via nginx.