Index: src/wp-includes/functions.php
===================================================================
--- src/wp-includes/functions.php	(revision 29021)
+++ src/wp-includes/functions.php	(working copy)
@@ -2612,6 +2612,42 @@
 }
 
 /**
+ * Encode a variable into JSON, with some sanity checks
+ *
+ * @since 4.0
+ * 
+ * @param mixed $data Variable (usually an array or object) to encode as JSON
+ *
+ * @return string The JSON encoded string
+ */
+function wp_json_encode( $data ) {
+	$try = json_encode( $data );
+	
+	if ( ! empty( $try ) ) {
+		// If json_encode was successful, no need to do more sanity checking
+		return $try;
+	}
+
+	return json_encode( _wp_json_sanity_check( $data ) );
+}
+
+function _wp_json_sanity_check( $data ) {
+	if ( is_string( $data ) ) {
+		$data = wp_check_invalid_utf8( $data, true );
+	} else if ( is_array( $data ) ) {
+		foreach ( $data as $id => $el ) {
+			$data[ $id ] = _wp_json_sanity_check( $el, $_depth + 1 );
+		}
+	} else if ( is_object( $data ) ) {
+		foreach ( $data as $id => $el ) {
+			$data->$id = _wp_json_sanity_check( $el, $_depth + 1 );
+		}
+	}
+
+	return $data;
+}
+
+/**
  * Send a JSON response back to an Ajax request.
  *
  * @since 3.5.0
@@ -2621,7 +2657,7 @@
  */
 function wp_send_json( $response ) {
 	@header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
-	echo json_encode( $response );
+	echo wp_json_encode( $response );
 	if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
 		wp_die();
 	else
