Lately, I’ve been working on rewriting the API for Wufoo, a task which requires emitting deeply nested XML and JSON strings from PHP. I’ve found writing — and religiously using — a class library beats using hand-coded string concatenation hands down. Also, I found a useful TextMate JSON bundle for prettifying your output. I’ll share my experience with each here.

Creating JSON

In the following examples, we’ll be creating the following JSON string:

{"submission": {"people":[
    {"person":{"sex":"male", "fullName":"Timmy \"Two Toes\" Sabat"}},
    {"person":{"sex":"female","fullName":"Camden \"Butter Bean\" Sabat"}}
]}}

To code this string without a library, you could simply do this:

$json = '{"submission": {"people":[';
foreach($this->Subfields as $key => $value){
    $json.= '{"person":{"sex":"' . $value->Sex . '",' .
    '"fullName":"' . StringUtil::JsonClean($value->FullName) . '"' .
    '}}';
}
$json.= ']}}';

But I find concatenating strings, like in the example above, leads to code that’s difficult to read, review and rewrite. I like to think of this as the WORM (write once, read many) principal. While writing the fewest number of keystrokes may save you time initially, those reading it will appreciate self-documented code. Since code will be read many more times than you’ll write it (think code-reviews or enhancements) you’re better off using a well-written class.

The following is an example of the same result hidden within an intension-revealing class.

foreach($Subfields as $key => $value){
    $subs.= JsonUtil::beginObject('person');
    $subs.= JsonUtil::value('sex',$value->Sex);
    $subs.= JsonUtil::valueWithoutTrailingCommaEscaped('fullName', $value->FullName);
    $subs.= JsonUtil::endObject();
}
$json = JsonUtil::fullObject('submission', JsonUtil::collection('people', $subs));

While this code takes more keystrokes to write, it is easier to enhance or review. When your JSON strings get long and nested, you’ll also love writing something that reads more legibly.

Verifying JSON

Once you write your JSON string, you want to verify it. I’ve been using a nice TextMate bundle which can be found here. Once installed, it forces long, unruly JSON strings to behave by adding line-breaks and indentation. I find this bundle very useful for seeing my JSON structure.

Below, you can see what our JSON string looks like before and after the bundle has had it’s way with it.

Before JSON bundle processing

Before Bundle Processing

After JSON bundle processing

After Bundle Processing

The Library

I’ve included my class library for JSON below. Remember, you’ll have to replace the String::jsonClean function with your own to make this example work.

class JsonUtil {
    public static function beginObject($objectName){
        return '{"'. $objectName . '":{';
    }    public static function endObject(){
        return '}}';
    }    public static function value($name, $value){
        return '"' . $name . '":"' . $value .'",';
    }    public static function valueEscaped($name, $value){
        return '"' . $name . '":"' . String::jsonClean($value) .'",';
    }    public static function valueWithoutTrailingComma($name, $value){
        return '"' . $name . '":"' . $value .'"';
    }    public static function valueWithoutTrailingCommaEscaped($name, $value){
        return '"' . $name . '":"' . String::jsonClean($value) .'"';
    }    public static function collection ($name, $arraySubstring){
        return '"' . $name . '":[' . $arraySubstring . ']';
    }    public static function fullObject($objectName, $values){
        return self::beginObject($objectName) .
            $values . self::endObject();
    }
}

Hopefully, these tips will help you create code that is easier to read, review and rewrite.

UPDATE:

In the comments, some of you have suggested we use json_encode for the heavy-lifting of json creation. That function is perfect when you’re using an object as if it is a simple hash-map and want to reproduce it exactly. But, in our case, we need more control over the output than json-encode will supply. With Wufoo’s API, we’re dealing with a deeply nested data structure built from PHP objects. Some of these objects contain properties that we can’t share with the rest of the world (e.g. $Password). This data would be included along with the rest of the object graph if we used json_encode on the base object.

We’ve discussed using clone to shallow copy the object, then eliminate the unwanted member variables and use json_encode on that specific object, but we ran into the overhead of PHP’s awkward object-creation memory consumption.

A less memory-intensive alternative to cloning would be to create an array of a subset of the object’s member variables and then json_encode these. This may be a strong solution, but it still requires custom code, much like the JsonUtil class shown in the example.

HTML Form Builder
Tim Sabat

Using a Custom Class To Create JSON in PHP by Tim Sabat

This entry was posted 2 years ago and was filed under Notebooks.
Comments are currently closed.

· 10 Comments! ·

  1. Jeff · 2 years ago

    Can you go back to the part of the article where you explain why you are using this class instead of creating beautiful Object/Array sets and simply using json_encode()?

    I mean:

    $data = new StdClass(); // initiating objects in php is kinda ugly... $data->submission = $Subfields; $json = json_encode($data);

    Done?

  2. Ryan Parman · 2 years ago

    Yeah, json_encode() is built into PHP 5.2.x and is much, much better.

  3. Ben · 2 years ago

    I’m with Jeff, why not use a standard object/array and seralize it. Like in this class: http://cesars.users.phpclasses.org/browse/file/20928.html

  4. Zach · 2 years ago

    I agree, json_encode() is the way to go. Since you probably already have the data in an array/object, you can do it in one line. I’ve used in on a number of projects, and works great. Thanks for the tip about the TextMate JSON bundle, that will be helpful for debugging.

  5. Maciej ?ebkowski · 2 years ago

    In addition, in pre-5.2.x there was an external module for PHP, that added json_encode/decode functions. So even for not up-to-date installations there`s a really simple solution.

    Cheers.

  6. Kr0n · 2 years ago

    Right, another one for json_encode()

    Would like to know though what’re Tim’s reasons to use the article class, instead of json_encode ;)

    I use print_r to beautify json outputs, with a debug flag to turn it on. Tumblr guys do this also (http://www.tumblr.com/api)

  7. Tim Sabat · 2 years ago

    Hey guys, thanks for keeping me on my toes! Because of the overwhelming outcry of love for json_encode, I’ve posted an update to my article explaining why I chose to write a custom Json class. Read about it above.

  8. RODGER · 2 years ago

    Everyone needs a hug. A GOOD HUG CAN MAKE ONES LIFE SEEM LIVEABLE AND WILL IMPROVE SOON!

  9. Simon Willison · 2 years ago

    “A less memory-intensive alternative to cloning would be to create an array of a subset of the object’s member variables and then json_encode these. This may be a strong solution, but it still requires custom code, much like the JsonUtil class shown in the example.” - yes, but it’s absolutely fundamentally The Right Way to do this. The whole point of JSON is that it lets you transfer data structures between different languages. The idea is that you create the data structure in your language and then call an encoding function to turn it in to a JSON string. That’s why JSON libraries exist! There really is no need to create an XML-style builder library to do this.

  10. Steve Clay · 2 years ago

    @Jeff: You don’t need StdClass to get JSON objects, just an array with a non-numeric key.