diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php
index afc8d50..b4ea4d1 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php
@@ -53,8 +53,7 @@ public function get($id = NULL) {
       if (empty($result)) {
         throw new NotFoundHttpException('Not Found');
       }
-      // @todo remove hard coded format here.
-      return new Response(drupal_json_encode($result[0]), 200, array('Content-Type' => 'application/json'));
+      return $result[0];
     }
   }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
index 3524389..8416371 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
@@ -26,6 +26,26 @@
 class EntityResource extends ResourceBase {
 
   /**
+   * Responds to entity GET requests.
+   *
+   * @param mixed $id
+   *   The entity ID.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   The response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
+   */
+  public function get($id) {
+    $definition = $this->getDefinition();
+    $entity = entity_load($definition['entity_type'], $id);
+    if ($entity) {
+      return $entity;
+    }
+    throw new NotFoundHttpException(t('Entity with ID @id not found', array('@id' => $id)));
+  }
+
+  /**
    * Responds to entity DELETE requests.
    *
    * @param mixed $id
diff --git a/core/modules/rest/lib/Drupal/rest/RequestHandler.php b/core/modules/rest/lib/Drupal/rest/RequestHandler.php
index 6bea36a..16af1d5 100644
--- a/core/modules/rest/lib/Drupal/rest/RequestHandler.php
+++ b/core/modules/rest/lib/Drupal/rest/RequestHandler.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\rest;
 
+use Drupal\Core\Entity\EntityNG;
 use Symfony\Component\DependencyInjection\ContainerAware;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
@@ -39,7 +40,22 @@ public function handle($plugin, Request $request, $id = NULL) {
         ->get('plugin.manager.rest')
         ->getInstance(array('id' => $plugin));
       try {
-        return $resource->{$method}($id);
+        $data = $resource->{$method}($id);
+        // If the plugin returns response objects itself we pass them through.
+        if ($data instanceof Response) {
+          return $data;
+        }
+        // Otherwise we serialize the data.
+        $serializer = $this->container->get('serializer');
+        // Convert object to array as normalizer does not support
+        // non EntityNG objects.
+        if (is_object($data) && !$data instanceof EntityNG) {
+          $data = (array) $data;
+        }
+        // @todo Replace the format here with something we get from the HTTP
+        //   Accept headers. See http://drupal.org/node/1833440
+        $output = $serializer->serialize($data, 'drupal_jsonld');
+        return new Response($output, 200, array('Content-Type' => 'application/ld+json'));
       }
       catch (HttpException $e) {
         return new Response($e->getMessage(), $e->getStatusCode(), $e->getHeaders());
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
index 6bd8e65..3d995d7 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
@@ -19,7 +19,7 @@ class DBLogTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('rest', 'dblog');
+  public static $modules = array('jsonld', 'rest', 'dblog');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
index 187c386..8e27864 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
@@ -34,18 +34,7 @@ public static function getInfo() {
    */
   public function testDelete() {
     foreach (entity_get_info() as $entity_type => $info) {
-      // Enable web API for this entity type.
-      $config = config('rest');
-      $config->set('resources', array(
-        'entity:' . $entity_type => 'entity:' . $entity_type,
-      ));
-      $config->save();
-
-      // Rebuild routing cache, so that the web API paths are available.
-      drupal_container()->get('router.builder')->rebuild();
-      // Reset the Simpletest permission cache, so that the new resource
-      // permissions get picked up.
-      drupal_static_reset('checkPermissions');
+      $this->enableService($entity_type);
       // Create a user account that has the required permissions to delete
       // resources via the web API.
       $account = $this->drupalCreateUser(array('restful delete entity:' . $entity_type));
@@ -81,6 +70,7 @@ public function testDelete() {
       $this->assertNotIdentical(FALSE, entity_load($entity_type, $entity->id(), TRUE), 'The ' . $entity_type . ' entity is still in the database.');
     }
     // Try to delete a resource which is not web API enabled.
+    $this->enableService(FALSE);
     $account = $this->drupalCreateUser();
     // Reset cURL here because it is confused from our previously used cURL
     // options.
@@ -88,32 +78,7 @@ public function testDelete() {
     $this->drupalLogin($account);
     $this->httpRequest('entity/user/' . $account->id(), 'DELETE');
     $user = entity_load('user', $account->id(), TRUE);
-    $this->assertEqual($account->id(), $user->id());
+    $this->assertEqual($account->id(), $user->id(), 'User still exists in the database.');
     $this->assertResponse(404);
   }
-
-  /**
-   * Creates entity objects based on their types.
-   *
-   * Required properties differ from entity type to entity type, so we keep a
-   * minimum mapping here.
-   *
-   * @param string $entity_type
-   *   The type of the entity that should be created..
-   *
-   * @return \Drupal\Core\Entity\EntityInterface
-   *   The new entity object.
-   */
-  protected function entityCreate($entity_type) {
-    switch ($entity_type) {
-      case 'entity_test':
-        return entity_create('entity_test', array('name' => 'test', 'user_id' => 1));
-      case 'node':
-        return entity_create('node', array('title' => $this->randomString()));
-      case 'user':
-        return entity_create('user', array('name' => $this->randomName()));
-      default:
-        return entity_create($entity_type, array());
-    }
-  }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
index 3bc8756..1dbc796 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
@@ -31,13 +31,14 @@ protected function httpRequest($url, $method, $body = NULL, $format = 'applicati
       case 'GET':
         // Set query if there are additional GET parameters.
         $options = isset($body) ? array('absolute' => TRUE, 'query' => $body) : array('absolute' => TRUE);
-        return $this->curlExec(array(
+        $response = $this->curlExec(array(
           CURLOPT_HTTPGET => TRUE,
           CURLOPT_URL => url($url, $options),
           CURLOPT_NOBODY => FALSE)
         );
+        break;
       case 'POST':
-        return $this->curlExec(array(
+        $response = $this->curlExec(array(
           CURLOPT_HTTPGET => FALSE,
           CURLOPT_POST => TRUE,
           CURLOPT_POSTFIELDS => $body,
@@ -45,8 +46,9 @@ protected function httpRequest($url, $method, $body = NULL, $format = 'applicati
           CURLOPT_NOBODY => FALSE,
           CURLOPT_HTTPHEADER => array('Content-Type: ' . $format),
         ));
+        break;
       case 'PUT':
-        return $this->curlExec(array(
+        $response = $this->curlExec(array(
           CURLOPT_HTTPGET => FALSE,
           CURLOPT_CUSTOMREQUEST => 'PUT',
           CURLOPT_POSTFIELDS => $body,
@@ -54,13 +56,82 @@ protected function httpRequest($url, $method, $body = NULL, $format = 'applicati
           CURLOPT_NOBODY => FALSE,
           CURLOPT_HTTPHEADER => array('Content-Type: ' . $format),
         ));
+        break;
       case 'DELETE':
-        return $this->curlExec(array(
+        $response = $this->curlExec(array(
           CURLOPT_HTTPGET => FALSE,
           CURLOPT_CUSTOMREQUEST => 'DELETE',
           CURLOPT_URL => url($url, array('absolute' => TRUE)),
           CURLOPT_NOBODY => FALSE,
         ));
+        break;
     }
+
+    $this->verboseHttpRequest($url, $method, $body);
+
+    return $response;
+  }
+
+  /**
+   * Creates entity objects based on their types.
+   *
+   * Required properties differ from entity type to entity type, so we keep a
+   * minimum mapping here.
+   *
+   * @param string $entity_type
+   *   The type of the entity that should be created..
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The new entity object.
+   */
+  protected function entityCreate($entity_type) {
+    switch ($entity_type) {
+      case 'entity_test':
+        return entity_create('entity_test', array('name' => $this->randomName(), 'user_id' => 1));
+      case 'node':
+        return entity_create('node', array('title' => $this->randomString()));
+      case 'user':
+        return entity_create('user', array('name' => $this->randomName()));
+      default:
+        return entity_create($entity_type, array());
+    }
+  }
+
+  /**
+   * Enables the web service interface for a specific entity type.
+   *
+   * @param string|FALSE $entity_type
+   *   The entity type that should get web API enabled or FALSE to disable all
+   *   entity types.
+   */
+  protected function enableService($entity_type) {
+    // Enable web API for this entity type.
+    $config = config('rest');
+    if ($entity_type) {
+      $config->set('resources', array(
+        'entity:' . $entity_type => 'entity:' . $entity_type,
+      ));
+    }
+    else {
+      $config->set('resources', array());
+    }
+    $config->save();
+
+    // Rebuild routing cache, so that the web API paths are available.
+    drupal_container()->get('router.builder')->rebuild();
+    // Reset the Simpletest permission cache, so that the new resource
+    // permissions get picked up.
+    drupal_static_reset('checkPermissions');
+  }
+
+  /**
+   * Verbose the response of the REST call.
+   */
+  protected function verboseHttpRequest($url, $method, $body) {
+    $response_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
+    $response_content = $this->drupalGetContent();
+    $this->verbose($method . ' request to: ' . $url .
+      '<hr />Code: ' . $response_code .
+      '<hr />Response: ' . $response_content);
   }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
new file mode 100644
index 0000000..37750c8
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\rest\test\ReadTest.
+ */
+
+namespace Drupal\rest\Tests;
+
+use Drupal\rest\Tests\RESTTestBase;
+
+/**
+ * Tests resource read operations on test entities, nodes and users.
+ */
+class ReadTest extends RESTTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('jsonld', 'rest', 'entity_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Read resource',
+      'description' => 'Tests the retrieval of resources.',
+      'group' => 'REST',
+    );
+  }
+
+  /**
+   * Tests several valid and invalid read requests on all entity types.
+   */
+  public function testRead() {
+    // @todo once EntityNG is implemented for other entity types use the full
+    // entity_get_info() for all entity types here.
+    $entity_test_info = entity_get_info('entity_test');
+    $entity_info = array('entity_test' => $entity_test_info);
+    foreach ($entity_info as $entity_type => $info) {
+      $this->enableService($entity_type);
+      // Create a user account that has the required permissions to delete
+      // resources via the web API.
+      $account = $this->drupalCreateUser(array('restful get entity:' . $entity_type));
+      // Reset cURL here because it is confused from our previously used cURL
+      // options.
+      unset($this->curlHandle);
+      $this->drupalLogin($account);
+
+      // Create an entity programmatically.
+      $entity = $this->entityCreate($entity_type);
+      $entity->save();
+      // Read it over the web API.
+      $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/ld+json');
+      $this->assertResponse('200', 'HTTP response code is correct.');
+      $data = drupal_json_decode($response);
+      // Only assert one example property here, other properties should be
+      // checked in serialization tests.
+      $this->assertEqual($data['uuid'][LANGUAGE_DEFAULT][0]['value'], $entity->uuid(), 'Entity UUID is correct');
+
+      // Try to read an entity that does not exist.
+      $response = $this->httpRequest('entity/' . $entity_type . '/9999', 'GET', NULL, 'application/ld+json');
+      $this->assertResponse(404);
+      $this->assertEqual($response, 'Entity with ID 9999 not found', 'Response message is correct.');
+
+      // Try to read an entity without proper permissions.
+      $this->drupalLogout();
+      $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/ld+json');
+      $this->assertResponse(403);
+      $this->assertNull(drupal_json_decode($response), 'No valid JSON found.');
+    }
+    // Try to read a resource which is not web API enabled.
+    $account = $this->drupalCreateUser();
+    // Reset cURL here because it is confused from our previously used cURL
+    // options.
+    unset($this->curlHandle);
+    $this->drupalLogin($account);
+    $response = $this->httpRequest('entity/user/' . $account->id(), 'GET', NULL, 'application/ld+json');
+    $this->assertResponse(404);
+    $this->assertNull(drupal_json_decode($response), 'No valid JSON found.');
+  }
+}
diff --git a/core/modules/rest/rest.info b/core/modules/rest/rest.info
index 56c650c..8513e15 100644
--- a/core/modules/rest/rest.info
+++ b/core/modules/rest/rest.info
@@ -3,4 +3,5 @@ description = Exposes entities and other resources as RESTful web API
 package = Core
 version = VERSION
 core = 8.x
+dependencies[] = jsonld
 configure = admin/config/services/rest
