remove mapstructure from identity,networking,objectstorage,orchestration,pagination
diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/extensions/admin/roles/results.go
index 6cfe3ac..608f206 100644
--- a/openstack/identity/v2/extensions/admin/roles/results.go
+++ b/openstack/identity/v2/extensions/admin/roles/results.go
@@ -1,7 +1,6 @@
 package roles
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -29,21 +28,17 @@
 // IsEmpty determines whether or not a page of Tenants contains any results.
 func (page RolePage) IsEmpty() (bool, error) {
 	users, err := ExtractRoles(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // ExtractRoles returns a slice of roles contained in a single page of results.
 func ExtractRoles(page pagination.Page) ([]Role, error) {
-	casted := page.(RolePage).Body
-	var response struct {
-		Roles []Role `mapstructure:"roles"`
+	r := page.(RolePage)
+	var s struct {
+		Roles []Role `json:"roles"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Roles, err
+	err := r.ExtractInto(&s)
+	return s.Roles, err
 }
 
 // UserRoleResult represents the result of either an AddUserRole or
diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/delegate.go
index e6af80e..4b2c6a7 100644
--- a/openstack/identity/v2/extensions/delegate.go
+++ b/openstack/identity/v2/extensions/delegate.go
@@ -1,7 +1,6 @@
 package extensions
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
 	"github.com/gophercloud/gophercloud/pagination"
@@ -24,16 +23,15 @@
 // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
 // elements into a slice of Extension structs.
 func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
+	r := page.(ExtensionPage)
 	// Identity v2 adds an intermediate "values" object.
-
-	var resp struct {
+	var s struct {
 		Extensions struct {
-			Values []common.Extension `mapstructure:"values"`
-		} `mapstructure:"extensions"`
+			Values []common.Extension `json:"values"`
+		} `json:"extensions"`
 	}
-
-	err := mapstructure.Decode(page.(ExtensionPage).Body, &resp)
-	return resp.Extensions.Values, err
+	err := r.ExtractInto(&s)
+	return s.Extensions.Values, err
 }
 
 // Get retrieves information for a specific extension using its alias.
diff --git a/openstack/identity/v2/tenants/results.go b/openstack/identity/v2/tenants/results.go
index cee7568..bf52554 100644
--- a/openstack/identity/v2/tenants/results.go
+++ b/openstack/identity/v2/tenants/results.go
@@ -1,7 +1,6 @@
 package tenants
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -9,16 +8,16 @@
 // Tenant is a grouping of users in the identity service.
 type Tenant struct {
 	// ID is a unique identifier for this tenant.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name is a friendlier user-facing name for this tenant.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Description is a human-readable explanation of this Tenant's purpose.
-	Description string `mapstructure:"description"`
+	Description string `json:"description"`
 
 	// Enabled indicates whether or not a tenant is active.
-	Enabled bool `mapstructure:"enabled"`
+	Enabled bool `json:"enabled"`
 }
 
 // TenantPage is a single page of Tenant results.
@@ -29,34 +28,27 @@
 // IsEmpty determines whether or not a page of Tenants contains any results.
 func (page TenantPage) IsEmpty() (bool, error) {
 	tenants, err := ExtractTenants(page)
-	if err != nil {
-		return false, err
-	}
-	return len(tenants) == 0, nil
+	return len(tenants) == 0, err
 }
 
 // NextPageURL extracts the "next" link from the tenants_links section of the result.
 func (page TenantPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"tenants_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"tenants_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractTenants returns a slice of Tenants contained in a single page of results.
 func ExtractTenants(page pagination.Page) ([]Tenant, error) {
-	casted := page.(TenantPage).Body
-	var response struct {
-		Tenants []Tenant `mapstructure:"tenants"`
+	r := page.(TenantPage)
+	var s struct {
+		Tenants []Tenant `json:"tenants"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Tenants, err
+	err := r.ExtractInto(&s)
+	return s.Tenants, err
 }
diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go
index 9ef45b6..93c0554 100644
--- a/openstack/identity/v2/tokens/results.go
+++ b/openstack/identity/v2/tokens/results.go
@@ -3,7 +3,6 @@
 import (
 	"time"
 
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
 )
@@ -25,15 +24,17 @@
 	Tenant tenants.Tenant
 }
 
-// Authorization need user info which can get from token authentication's response
+// Role is a role for a user.
 type Role struct {
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 }
+
+// User is an OpenStack user.
 type User struct {
-	ID       string `mapstructure:"id"`
-	Name     string `mapstructure:"name"`
-	UserName string `mapstructure:"username"`
-	Roles    []Role `mapstructure:"roles"`
+	ID       string `json:"id"`
+	Name     string `json:"name"`
+	UserName string `json:"username"`
+	Roles    []Role `json:"roles"`
 }
 
 // Endpoint represents a single API endpoint offered by a service.
@@ -45,14 +46,14 @@
 //
 // In all cases, fields which aren't supported by the provider and service combined will assume a zero-value ("").
 type Endpoint struct {
-	TenantID    string `mapstructure:"tenantId"`
-	PublicURL   string `mapstructure:"publicURL"`
-	InternalURL string `mapstructure:"internalURL"`
-	AdminURL    string `mapstructure:"adminURL"`
-	Region      string `mapstructure:"region"`
-	VersionID   string `mapstructure:"versionId"`
-	VersionInfo string `mapstructure:"versionInfo"`
-	VersionList string `mapstructure:"versionList"`
+	TenantID    string `json:"tenantId"`
+	PublicURL   string `json:"publicURL"`
+	InternalURL string `json:"internalURL"`
+	AdminURL    string `json:"adminURL"`
+	Region      string `json:"region"`
+	VersionID   string `json:"versionId"`
+	VersionInfo string `json:"versionInfo"`
+	VersionList string `json:"versionList"`
 }
 
 // CatalogEntry provides a type-safe interface to an Identity API V2 service catalog listing.
@@ -63,15 +64,15 @@
 // Otherwise, you'll tie the representation of the service to a specific provider.
 type CatalogEntry struct {
 	// Name will contain the provider-specified name for the service.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Type will contain a type string if OpenStack defines a type for the service.
 	// Otherwise, for provider-specific services, the provider may assign their own type strings.
-	Type string `mapstructure:"type"`
+	Type string `json:"type"`
 
 	// Endpoints will let the caller iterate over all the different endpoints that may exist for
 	// the service.
-	Endpoints []Endpoint `mapstructure:"endpoints"`
+	Endpoints []Endpoint `json:"endpoints"`
 }
 
 // ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
@@ -92,56 +93,43 @@
 }
 
 // ExtractToken returns the just-created Token from a CreateResult.
-func (result CreateResult) ExtractToken() (*Token, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractToken() (*Token, error) {
+	var s struct {
 		Access struct {
 			Token struct {
-				Expires string         `mapstructure:"expires"`
-				ID      string         `mapstructure:"id"`
-				Tenant  tenants.Tenant `mapstructure:"tenant"`
-			} `mapstructure:"token"`
-		} `mapstructure:"access"`
+				Expires string         `json:"expires"`
+				ID      string         `json:"id"`
+				Tenant  tenants.Tenant `json:"tenant"`
+			} `json:"token"`
+		} `json:"access"`
 	}
 
-	err := mapstructure.Decode(result.Body, &response)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return nil, err
 	}
 
-	expiresTs, err := time.Parse(gophercloud.RFC3339Milli, response.Access.Token.Expires)
+	expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires)
 	if err != nil {
 		return nil, err
 	}
 
 	return &Token{
-		ID:        response.Access.Token.ID,
+		ID:        s.Access.Token.ID,
 		ExpiresAt: expiresTs,
-		Tenant:    response.Access.Token.Tenant,
+		Tenant:    s.Access.Token.Tenant,
 	}, nil
 }
 
 // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
-func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
+	var s struct {
 		Access struct {
-			Entries []CatalogEntry `mapstructure:"serviceCatalog"`
-		} `mapstructure:"access"`
+			Entries []CatalogEntry `json:"serviceCatalog"`
+		} `json:"access"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ServiceCatalog{Entries: response.Access.Entries}, nil
+	err := r.ExtractInto(&s)
+	return &ServiceCatalog{Entries: s.Access.Entries}, err
 }
 
 // createErr quickly packs an error in a CreateResult.
@@ -150,21 +138,12 @@
 }
 
 // ExtractUser returns the User from a GetResult.
-func (result GetResult) ExtractUser() (*User, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r GetResult) ExtractUser() (*User, error) {
+	var s struct {
 		Access struct {
-			User User `mapstructure:"user"`
-		} `mapstructure:"access"`
+			User User `json:"user"`
+		} `json:"access"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &response.Access.User, nil
+	err := r.ExtractInto(&s)
+	return &s.Access.User, err
 }
diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go
index 37ba1be..c353c61 100644
--- a/openstack/identity/v2/users/results.go
+++ b/openstack/identity/v2/users/results.go
@@ -1,8 +1,6 @@
 package users
 
 import (
-	"github.com/mitchellh/mapstructure"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -25,7 +23,7 @@
 	Email string
 
 	// The ID of the tenant to which this user belongs.
-	TenantID string `mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // Role assigns specific responsibilities to users, allowing them to accomplish
@@ -51,41 +49,33 @@
 // IsEmpty determines whether or not a page of Tenants contains any results.
 func (page UserPage) IsEmpty() (bool, error) {
 	users, err := ExtractUsers(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // ExtractUsers returns a slice of Tenants contained in a single page of results.
 func ExtractUsers(page pagination.Page) ([]User, error) {
-	casted := page.(UserPage).Body
-	var response struct {
-		Users []User `mapstructure:"users"`
+	r := page.(UserPage)
+	var s struct {
+		Users []User `json:"users"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Users, err
+	err := r.ExtractInto(&s)
+	return s.Users, err
 }
 
 // IsEmpty determines whether or not a page of Tenants contains any results.
 func (page RolePage) IsEmpty() (bool, error) {
 	users, err := ExtractRoles(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // ExtractRoles returns a slice of Roles contained in a single page of results.
 func ExtractRoles(page pagination.Page) ([]Role, error) {
-	casted := page.(RolePage).Body
-	var response struct {
-		Roles []Role `mapstructure:"roles"`
+	r := page.(RolePage)
+	var s struct {
+		Roles []Role `json:"roles"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Roles, err
+	err := r.ExtractInto(&s)
+	return s.Roles, err
 }
 
 type commonResult struct {
@@ -94,17 +84,11 @@
 
 // Extract interprets any commonResult as a User, if possible.
 func (r commonResult) Extract() (*User, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		User *User `json:"user"`
 	}
-
-	var response struct {
-		User User `mapstructure:"user"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.User, err
+	err := r.ExtractInto(&s)
+	return s.User, err
 }
 
 // CreateResult represents the result of a Create operation
diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go
index 7c20b8c..09e58e5 100644
--- a/openstack/identity/v3/endpoints/results.go
+++ b/openstack/identity/v3/endpoints/results.go
@@ -1,7 +1,6 @@
 package endpoints
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -13,17 +12,11 @@
 // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Endpoint.
 // An error is returned if the original call or the extraction failed.
 func (r commonResult) Extract() (*Endpoint, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Endpoint *Endpoint `json:"endpoint"`
 	}
-
-	var res struct {
-		Endpoint `json:"endpoint"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res.Endpoint, err
+	err := r.ExtractInto(&s)
+	return s.Endpoint, err
 }
 
 // CreateResult is the deferred result of a Create call.
@@ -48,12 +41,12 @@
 
 // Endpoint describes the entry point for another service's API.
 type Endpoint struct {
-	ID           string                   `mapstructure:"id" json:"id"`
-	Availability gophercloud.Availability `mapstructure:"interface" json:"interface"`
-	Name         string                   `mapstructure:"name" json:"name"`
-	Region       string                   `mapstructure:"region" json:"region"`
-	ServiceID    string                   `mapstructure:"service_id" json:"service_id"`
-	URL          string                   `mapstructure:"url" json:"url"`
+	ID           string                   `json:"id"`
+	Availability gophercloud.Availability `json:"interface"`
+	Name         string                   `json:"name"`
+	Region       string                   `json:"region"`
+	ServiceID    string                   `json:"service_id"`
+	URL          string                   `json:"url"`
 }
 
 // EndpointPage is a single page of Endpoint results.
@@ -64,19 +57,15 @@
 // IsEmpty returns true if no Endpoints were returned.
 func (p EndpointPage) IsEmpty() (bool, error) {
 	es, err := ExtractEndpoints(p)
-	if err != nil {
-		return true, err
-	}
-	return len(es) == 0, nil
+	return len(es) == 0, err
 }
 
 // ExtractEndpoints extracts an Endpoint slice from a Page.
 func ExtractEndpoints(page pagination.Page) ([]Endpoint, error) {
-	var response struct {
-		Endpoints []Endpoint `mapstructure:"endpoints"`
+	r := page.(EndpointPage)
+	var s struct {
+		Endpoints []Endpoint `json:"endpoints"`
 	}
-
-	err := mapstructure.Decode(page.(EndpointPage).Body, &response)
-
-	return response.Endpoints, err
+	err := r.ExtractInto(&s)
+	return s.Endpoints, err
 }
diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go
index 75549d3..86f21a0 100644
--- a/openstack/identity/v3/roles/results.go
+++ b/openstack/identity/v3/roles/results.go
@@ -1,10 +1,6 @@
 package roles
 
-import (
-	"github.com/gophercloud/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
-)
+import "github.com/gophercloud/gophercloud/pagination"
 
 // RoleAssignment is the result of a role assignments query.
 type RoleAssignment struct {
@@ -20,7 +16,7 @@
 
 type Scope struct {
 	Domain  Domain  `json:"domain,omitempty"`
-	Project Project `json:"domain,omitempty"`
+	Project Project `json:"project,omitempty"`
 }
 
 type Domain struct {
@@ -47,35 +43,26 @@
 // IsEmpty returns true if the page contains no results.
 func (p RoleAssignmentsPage) IsEmpty() (bool, error) {
 	roleAssignments, err := ExtractRoleAssignments(p)
-	if err != nil {
-		return true, err
-	}
-	return len(roleAssignments) == 0, nil
+	return len(roleAssignments) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
 func (page RoleAssignmentsPage) NextPageURL() (string, error) {
-	type resp struct {
+	var s struct {
 		Links struct {
-			Next string `mapstructure:"next"`
-		} `mapstructure:"links"`
+			Next string `json:"next"`
+		} `json:"links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
-	if err != nil {
-		return "", err
-	}
-
-	return r.Links.Next, nil
+	err := page.ExtractInto(&s)
+	return s.Links.Next, err
 }
 
 // ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection acquired from List.
 func ExtractRoleAssignments(page pagination.Page) ([]RoleAssignment, error) {
-	var response struct {
-		RoleAssignments []RoleAssignment `mapstructure:"role_assignments"`
+	r := page.(RoleAssignmentsPage)
+	var s struct {
+		RoleAssignments []RoleAssignment `json:"role_assignments"`
 	}
-
-	err := mapstructure.Decode(page.(RoleAssignmentsPage).Body, &response)
-	return response.RoleAssignments, err
+	err := r.ExtractInto(&s)
+	return s.RoleAssignments, err
 }
diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go
index c55ee62..45040c6 100644
--- a/openstack/identity/v3/services/results.go
+++ b/openstack/identity/v3/services/results.go
@@ -3,8 +3,6 @@
 import (
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
 )
 
 type commonResult struct {
@@ -14,17 +12,11 @@
 // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Service.
 // An error is returned if the original call or the extraction failed.
 func (r commonResult) Extract() (*Service, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Service *Service `json:"service"`
 	}
-
-	var res struct {
-		Service `json:"service"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res.Service, err
+	err := r.ExtractInto(&s)
+	return s.Service, err
 }
 
 // CreateResult is the deferred result of a Create call.
@@ -63,18 +55,15 @@
 // IsEmpty returns true if the page contains no results.
 func (p ServicePage) IsEmpty() (bool, error) {
 	services, err := ExtractServices(p)
-	if err != nil {
-		return true, err
-	}
-	return len(services) == 0, nil
+	return len(services) == 0, err
 }
 
 // ExtractServices extracts a slice of Services from a Collection acquired from List.
 func ExtractServices(page pagination.Page) ([]Service, error) {
-	var response struct {
-		Services []Service `mapstructure:"services"`
+	r := page.(ServicePage)
+	var s struct {
+		Services []Service `json:"services"`
 	}
-
-	err := mapstructure.Decode(page.(ServicePage).Body, &response)
-	return response.Services, err
+	err := r.ExtractInto(&s)
+	return s.Services, err
 }
diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go
index ec98c1c..7dd2c0b 100644
--- a/openstack/identity/v3/tokens/results.go
+++ b/openstack/identity/v3/tokens/results.go
@@ -3,7 +3,6 @@
 import (
 	"time"
 
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 )
 
@@ -12,10 +11,10 @@
 // If supported, it contains a region specifier, again if provided.
 // The significance of the Region field will depend upon your provider.
 type Endpoint struct {
-	ID        string `mapstructure:"id"`
-	Region    string `mapstructure:"region"`
-	Interface string `mapstructure:"interface"`
-	URL       string `mapstructure:"url"`
+	ID        string `json:"id"`
+	Region    string `json:"region"`
+	Interface string `json:"interface"`
+	URL       string `json:"url"`
 }
 
 // CatalogEntry provides a type-safe interface to an Identity API V3 service catalog listing.
@@ -27,18 +26,18 @@
 type CatalogEntry struct {
 
 	// Service ID
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name will contain the provider-specified name for the service.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Type will contain a type string if OpenStack defines a type for the service.
 	// Otherwise, for provider-specific services, the provider may assign their own type strings.
-	Type string `mapstructure:"type"`
+	Type string `json:"type"`
 
 	// Endpoints will let the caller iterate over all the different endpoints that may exist for
 	// the service.
-	Endpoints []Endpoint `mapstructure:"endpoints"`
+	Endpoints []Endpoint `json:"endpoints"`
 }
 
 // ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
@@ -59,14 +58,10 @@
 
 // ExtractToken interprets a commonResult as a Token.
 func (r commonResult) ExtractToken() (*Token, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
+	var s struct {
 		Token struct {
-			ExpiresAt string `mapstructure:"expires_at"`
-		} `mapstructure:"token"`
+			ExpiresAt string `json:"expires_at"`
+		} `json:"token"`
 	}
 
 	var token Token
@@ -74,35 +69,26 @@
 	// Parse the token itself from the stored headers.
 	token.ID = r.Header.Get("X-Subject-Token")
 
-	err := mapstructure.Decode(r.Body, &response)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return nil, err
 	}
 
 	// Attempt to parse the timestamp.
-	token.ExpiresAt, err = time.Parse(gophercloud.RFC3339Milli, response.Token.ExpiresAt)
+	token.ExpiresAt, err = time.Parse(gophercloud.RFC3339Milli, s.Token.ExpiresAt)
 
 	return &token, err
 }
 
 // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
-func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
+	var s struct {
 		Token struct {
-			Entries []CatalogEntry `mapstructure:"catalog"`
-		} `mapstructure:"token"`
+			Entries []CatalogEntry `json:"catalog"`
+		} `json:"token"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ServiceCatalog{Entries: response.Token.Entries}, nil
+	err := r.ExtractInto(&s)
+	return &ServiceCatalog{Entries: s.Token.Entries}, err
 }
 
 // CreateResult defers the interpretation of a created token.
diff --git a/openstack/networking/v2/apiversions/results.go b/openstack/networking/v2/apiversions/results.go
index 556b9aa..eff4485 100644
--- a/openstack/networking/v2/apiversions/results.go
+++ b/openstack/networking/v2/apiversions/results.go
@@ -1,15 +1,14 @@
 package apiversions
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // APIVersion represents an API version for Neutron. It contains the status of
 // the API, and its unique ID.
 type APIVersion struct {
-	Status string `mapstructure:"status" json:"status"`
-	ID     string `mapstructure:"id" json:"id"`
+	Status string `son:"status"`
+	ID     string `json:"id"`
 }
 
 // APIVersionPage is the page returned by a pager when traversing over a
@@ -21,29 +20,24 @@
 // IsEmpty checks whether an APIVersionPage struct is empty.
 func (r APIVersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractAPIVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractAPIVersions takes a collection page, extracts all of the elements,
 // and returns them a slice of APIVersion structs. It is effectively a cast.
-func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
-	var resp struct {
-		Versions []APIVersion `mapstructure:"versions"`
+func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) {
+	var s struct {
+		Versions []APIVersion `json:"versions"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
-
-	return resp.Versions, err
+	err := (r.(APIVersionPage)).ExtractInto(&s)
+	return s.Versions, err
 }
 
 // APIVersionResource represents a generic API resource. It contains the name
 // of the resource and its plural collection name.
 type APIVersionResource struct {
-	Name       string `mapstructure:"name" json:"name"`
-	Collection string `mapstructure:"collection" json:"collection"`
+	Name       string `json:"name"`
+	Collection string `json:"collection"`
 }
 
 // APIVersionResourcePage is a concrete type which embeds the common
@@ -56,22 +50,17 @@
 // APIVersionResourcePage is empty or not.
 func (r APIVersionResourcePage) IsEmpty() (bool, error) {
 	is, err := ExtractVersionResources(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractVersionResources accepts a Page struct, specifically a
 // APIVersionResourcePage struct, and extracts the elements into a slice of
 // APIVersionResource structs. In other words, the collection is mapped into
 // a relevant slice.
-func ExtractVersionResources(page pagination.Page) ([]APIVersionResource, error) {
-	var resp struct {
-		APIVersionResources []APIVersionResource `mapstructure:"resources"`
+func ExtractVersionResources(r pagination.Page) ([]APIVersionResource, error) {
+	var s struct {
+		APIVersionResources []APIVersionResource `json:"resources"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionResourcePage).Body, &resp)
-
-	return resp.APIVersionResources, err
+	err := (r.(APIVersionResourcePage)).ExtractInto(&s)
+	return s.APIVersionResources, err
 }
diff --git a/openstack/networking/v2/extensions/external/results.go b/openstack/networking/v2/extensions/external/results.go
index c5d5f14..2c5173a 100644
--- a/openstack/networking/v2/extensions/external/results.go
+++ b/openstack/networking/v2/extensions/external/results.go
@@ -1,7 +1,6 @@
 package external
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -10,72 +9,69 @@
 // "external-net" extension.
 type NetworkExternal struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 
 	// Specifies whether the network is an external network or not.
-	External bool `mapstructure:"router:external" json:"router:external"`
-}
-
-func commonExtract(e error, response interface{}) (*NetworkExternal, error) {
-	if e != nil {
-		return nil, e
-	}
-
-	var res struct {
-		Network *NetworkExternal `json:"network"`
-	}
-
-	err := mapstructure.Decode(response, &res)
-
-	return res.Network, err
+	External bool `json:"router:external"`
 }
 
 // ExtractGet decorates a GetResult struct returned from a networks.Get()
 // function with extended attributes.
 func ExtractGet(r networks.GetResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractCreate decorates a CreateResult struct returned from a networks.Create()
 // function with extended attributes.
 func ExtractCreate(r networks.CreateResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractUpdate decorates a UpdateResult struct returned from a
 // networks.Update() function with extended attributes.
 func ExtractUpdate(r networks.UpdateResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractList accepts a Page struct, specifically a NetworkPage struct, and
 // extracts the elements into a slice of NetworkExternal structs. In other
 // words, a generic collection is mapped into a relevant slice.
 func ExtractList(page pagination.Page) ([]NetworkExternal, error) {
-	var resp struct {
-		Networks []NetworkExternal `mapstructure:"networks" json:"networks"`
+	r := page.(networks.NetworkPage)
+	var s struct {
+		Networks []NetworkExternal `json:"networks" json:"networks"`
 	}
-
-	err := mapstructure.Decode(page.(networks.NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := r.ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go
index 5af4b88..2b4338c 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go
@@ -1,19 +1,19 @@
 package firewalls
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
+// Firewall is an OpenStack firewall.
 type Firewall struct {
-	ID           string `json:"id" mapstructure:"id"`
-	Name         string `json:"name" mapstructure:"name"`
-	Description  string `json:"description" mapstructure:"description"`
-	AdminStateUp bool   `json:"admin_state_up" mapstructure:"admin_state_up"`
-	Status       string `json:"status" mapstructure:"status"`
-	PolicyID     string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
-	TenantID     string `json:"tenant_id" mapstructure:"tenant_id"`
+	ID           string `json:"id"`
+	Name         string `json:"name"`
+	Description  string `json:"description"`
+	AdminStateUp bool   `json:"admin_state_up"`
+	Status       string `json:"status"`
+	PolicyID     string `json:"firewall_policy_id"`
+	TenantID     string `json:"tenant_id"`
 }
 
 type commonResult struct {
@@ -22,17 +22,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall.
 func (r commonResult) Extract() (*Firewall, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Firewall *Firewall `json:"firewall"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Firewall, err
+	err := r.ExtractInto(&s)
+	return s.Firewall, err
 }
 
 // FirewallPage is the page returned by a pager when traversing over a
@@ -44,40 +38,32 @@
 // NextPageURL is invoked when a paginated collection of firewalls has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p FirewallPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewalls_links"`
+func (page FirewallPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewalls_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a FirewallPage struct is empty.
-func (p FirewallPage) IsEmpty() (bool, error) {
-	is, err := ExtractFirewalls(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page FirewallPage) IsEmpty() (bool, error) {
+	is, err := ExtractFirewalls(page)
+	return len(is) == 0, err
 }
 
 // ExtractFirewalls accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractFirewalls(page pagination.Page) ([]Firewall, error) {
-	var resp struct {
-		Firewalls []Firewall `mapstructure:"firewalls" json:"firewalls"`
+	var s struct {
+		Firewalls []Firewall `json:"firewalls" json:"firewalls"`
 	}
-
-	err := mapstructure.Decode(page.(FirewallPage).Body, &resp)
-
-	return resp.Firewalls, err
+	err := (page.(FirewallPage)).ExtractInto(&s)
+	return s.Firewalls, err
 }
 
 // GetResult represents the result of a get operation.
diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go
index 9a6d563..d23c81d 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/results.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/results.go
@@ -1,19 +1,19 @@
 package policies
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
+// Policy is a firewall policy.
 type Policy struct {
-	ID          string   `json:"id" mapstructure:"id"`
-	Name        string   `json:"name" mapstructure:"name"`
-	Description string   `json:"description" mapstructure:"description"`
-	TenantID    string   `json:"tenant_id" mapstructure:"tenant_id"`
-	Audited     bool     `json:"audited" mapstructure:"audited"`
-	Shared      bool     `json:"shared" mapstructure:"shared"`
-	Rules       []string `json:"firewall_rules,omitempty" mapstructure:"firewall_rules"`
+	ID          string   `json:"id"`
+	Name        string   `json:"name"`
+	Description string   `json:"description"`
+	TenantID    string   `json:"tenant_id"`
+	Audited     bool     `json:"audited"`
+	Shared      bool     `json:"shared"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
 type commonResult struct {
@@ -22,17 +22,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall policy.
 func (r commonResult) Extract() (*Policy, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Policy *Policy `json:"firewall_policy"`
 	}
-
-	var res struct {
-		Policy *Policy `json:"firewall_policy" mapstructure:"firewall_policy"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Policy, err
+	err := r.ExtractInto(&s)
+	return s.Policy, err
 }
 
 // PolicyPage is the page returned by a pager when traversing over a
@@ -44,40 +38,32 @@
 // NextPageURL is invoked when a paginated collection of firewall policies has
 // reached the end of a page and the pager seeks to traverse over a new one.
 // In order to do this, it needs to construct the next page's URL.
-func (p PolicyPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewall_policies_links"`
+func (page PolicyPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewall_policies_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PolicyPage struct is empty.
-func (p PolicyPage) IsEmpty() (bool, error) {
-	is, err := ExtractPolicies(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page PolicyPage) IsEmpty() (bool, error) {
+	is, err := ExtractPolicies(page)
+	return len(is) == 0, err
 }
 
 // ExtractPolicies accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractPolicies(page pagination.Page) ([]Policy, error) {
-	var resp struct {
-		Policies []Policy `mapstructure:"firewall_policies" json:"firewall_policies"`
+	var s struct {
+		Policies []Policy `json:"firewall_policies"`
 	}
-
-	err := mapstructure.Decode(page.(PolicyPage).Body, &resp)
-
-	return resp.Policies, err
+	err := (page.(PolicyPage)).ExtractInto(&s)
+	return s.Policies, err
 }
 
 // GetResult represents the result of a get operation.
diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go
index 42604f4..4afdf22 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/results.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/results.go
@@ -1,28 +1,27 @@
 package rules
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Rule represents a firewall rule
 type Rule struct {
-	ID                   string `json:"id" mapstructure:"id"`
-	Name                 string `json:"name,omitempty" mapstructure:"name"`
-	Description          string `json:"description,omitempty" mapstructure:"description"`
-	Protocol             string `json:"protocol" mapstructure:"protocol"`
-	Action               string `json:"action" mapstructure:"action"`
-	IPVersion            int    `json:"ip_version,omitempty" mapstructure:"ip_version"`
-	SourceIPAddress      string `json:"source_ip_address,omitempty" mapstructure:"source_ip_address"`
-	DestinationIPAddress string `json:"destination_ip_address,omitempty" mapstructure:"destination_ip_address"`
-	SourcePort           string `json:"source_port,omitempty" mapstructure:"source_port"`
-	DestinationPort      string `json:"destination_port,omitempty" mapstructure:"destination_port"`
-	Shared               bool   `json:"shared,omitempty" mapstructure:"shared"`
-	Enabled              bool   `json:"enabled,omitempty" mapstructure:"enabled"`
-	PolicyID             string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
-	Position             int    `json:"position" mapstructure:"position"`
-	TenantID             string `json:"tenant_id" mapstructure:"tenant_id"`
+	ID                   string `json:"id"`
+	Name                 string `json:"name,omitempty"`
+	Description          string `json:"description,omitempty"`
+	Protocol             string `json:"protocol"`
+	Action               string `json:"action"`
+	IPVersion            int    `json:"ip_version,omitempty"`
+	SourceIPAddress      string `json:"source_ip_address,omitempty"`
+	DestinationIPAddress string `json:"destination_ip_address,omitempty"`
+	SourcePort           string `json:"source_port,omitempty"`
+	DestinationPort      string `json:"destination_port,omitempty"`
+	Shared               bool   `json:"shared,omitempty"`
+	Enabled              bool   `json:"enabled,omitempty"`
+	PolicyID             string `json:"firewall_policy_id"`
+	Position             int    `json:"position"`
+	TenantID             string `json:"tenant_id"`
 }
 
 // RulePage is the page returned by a pager when traversing over a
@@ -34,40 +33,33 @@
 // NextPageURL is invoked when a paginated collection of firewall rules has
 // reached the end of a page and the pager seeks to traverse over a new one.
 // In order to do this, it needs to construct the next page's URL.
-func (p RulePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewall_rules_links"`
+func (page RulePage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewall_rules_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a RulePage struct is empty.
 func (p RulePage) IsEmpty() (bool, error) {
 	is, err := ExtractRules(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractRules accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractRules(page pagination.Page) ([]Rule, error) {
-	var resp struct {
-		Rules []Rule `mapstructure:"firewall_rules" json:"firewall_rules"`
+	r := page.(RulePage)
+	var s struct {
+		Rules []Rule `json:"firewall_rules"`
 	}
-
-	err := mapstructure.Decode(page.(RulePage).Body, &resp)
-
-	return resp.Rules, err
+	err := r.ExtractInto(&s)
+	return s.Rules, err
 }
 
 type commonResult struct {
@@ -76,17 +68,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall rule.
 func (r commonResult) Extract() (*Rule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Rule *Rule `json:"firewall_rule"`
 	}
-
-	var res struct {
-		Rule *Rule `json:"firewall_rule" mapstructure:"firewall_rule"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Rule, err
+	err := r.ExtractInto(&s)
+	return s.Rule, err
 }
 
 // GetResult represents the result of a get operation.
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go
index 264b7a8..c33b139 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/results.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go
@@ -1,9 +1,6 @@
 package floatingips
 
 import (
-	"fmt"
-
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -16,27 +13,27 @@
 // attribute (provided by the external network extension) is set to True.
 type FloatingIP struct {
 	// Unique identifier for the floating IP instance.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// UUID of the external network where the floating IP is to be created.
-	FloatingNetworkID string `json:"floating_network_id" mapstructure:"floating_network_id"`
+	FloatingNetworkID string `json:"floating_network_id"`
 
 	// Address of the floating IP on the external network.
-	FloatingIP string `json:"floating_ip_address" mapstructure:"floating_ip_address"`
+	FloatingIP string `json:"floating_ip_address"`
 
 	// UUID of the port on an internal network that is associated with the floating IP.
-	PortID string `json:"port_id" mapstructure:"port_id"`
+	PortID string `json:"port_id"`
 
 	// The specific IP address of the internal port which should be associated
 	// with the floating IP.
-	FixedIP string `json:"fixed_ip_address" mapstructure:"fixed_ip_address"`
+	FixedIP string `json:"fixed_ip_address"`
 
 	// Owner of the floating IP. Only admin users can specify a tenant identifier
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The condition of the API resource.
-	Status string `json:"status" mapstructure:"status"`
+	Status string `json:"status"`
 }
 
 type commonResult struct {
@@ -45,20 +42,11 @@
 
 // Extract a result and extracts a FloatingIP resource.
 func (r commonResult) Extract() (*FloatingIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		FloatingIP *FloatingIP `json:"floatingip"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	if err != nil {
-		return nil, fmt.Errorf("Error decoding Neutron floating IP: %v", err)
-	}
-
-	return res.FloatingIP, nil
+	err := r.ExtractInto(&s)
+	return s.FloatingIP, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -90,38 +78,31 @@
 // NextPageURL is invoked when a paginated collection of floating IPs has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p FloatingIPPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"floatingips_links"`
+func (page FloatingIPPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"floatingips_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a NetworkPage struct is empty.
-func (p FloatingIPPage) IsEmpty() (bool, error) {
-	is, err := ExtractFloatingIPs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page FloatingIPPage) IsEmpty() (bool, error) {
+	is, err := ExtractFloatingIPs(page)
+	return len(is) == 0, err
 }
 
 // ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage struct,
 // and extracts the elements into a slice of FloatingIP structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
-	var resp struct {
-		FloatingIPs []FloatingIP `mapstructure:"floatingips" json:"floatingips"`
+	r := page.(FloatingIPPage)
+	var s struct {
+		FloatingIPs []FloatingIP `json:"floatingips"`
 	}
-
-	err := mapstructure.Decode(page.(FloatingIPPage).Body, &resp)
-
-	return resp.FloatingIPs, err
+	err := r.ExtractInto(&s)
+	return s.FloatingIPs, err
 }
diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go
index 2360501..e26f243 100644
--- a/openstack/networking/v2/extensions/layer3/routers/results.go
+++ b/openstack/networking/v2/extensions/layer3/routers/results.go
@@ -1,7 +1,6 @@
 package routers
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -9,12 +8,13 @@
 // GatewayInfo represents the information of an external gateway for any
 // particular network router.
 type GatewayInfo struct {
-	NetworkID string `json:"network_id" mapstructure:"network_id"`
+	NetworkID string `json:"network_id"`
 }
 
+// Route is a possible route in a router.
 type Route struct {
-	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
-	DestinationCIDR string `mapstructure:"destination" json:"destination"`
+	NextHop         string `json:"nexthop"`
+	DestinationCIDR string `json:"destination"`
 }
 
 // Router represents a Neutron router. A router is a logical entity that
@@ -27,28 +27,28 @@
 // interface is added to the subnet's network.
 type Router struct {
 	// Indicates whether or not a router is currently operational.
-	Status string `json:"status" mapstructure:"status"`
+	Status string `json:"status"`
 
 	// Information on external gateway for the router.
-	GatewayInfo GatewayInfo `json:"external_gateway_info" mapstructure:"external_gateway_info"`
+	GatewayInfo GatewayInfo `json:"external_gateway_info"`
 
 	// Administrative state of the router.
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Whether router is disitrubted or not..
-	Distributed bool `json:"distributed" mapstructure:"distributed"`
+	Distributed bool `json:"distributed"`
 
 	// Human readable name for the router. Does not have to be unique.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Unique identifier for the router.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Owner of the router. Only admin users can specify a tenant identifier
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
-	Routes []Route `json:"routes" mapstructure:"routes"`
+	Routes []Route `json:"routes"`
 }
 
 // RouterPage is the page returned by a pager when traversing over a
@@ -60,40 +60,33 @@
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p RouterPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"routers_links"`
+func (page RouterPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"routers_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a RouterPage struct is empty.
-func (p RouterPage) IsEmpty() (bool, error) {
-	is, err := ExtractRouters(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page RouterPage) IsEmpty() (bool, error) {
+	is, err := ExtractRouters(page)
+	return len(is) == 0, err
 }
 
 // ExtractRouters accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractRouters(page pagination.Page) ([]Router, error) {
-	var resp struct {
-		Routers []Router `mapstructure:"routers" json:"routers"`
+	r := page.(RouterPage)
+	var s struct {
+		Routers []Router `json:"routers"`
 	}
-
-	err := mapstructure.Decode(page.(RouterPage).Body, &resp)
-
-	return resp.Routers, err
+	err := r.ExtractInto(&s)
+	return s.Routers, err
 }
 
 type commonResult struct {
@@ -102,17 +95,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Router, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Router *Router `json:"router"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Router, err
+	err := r.ExtractInto(&s)
+	return s.Router, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -140,16 +127,16 @@
 // interface.
 type InterfaceInfo struct {
 	// The ID of the subnet which this interface is associated with.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// The ID of the port that is a part of the subnet.
-	PortID string `json:"port_id" mapstructure:"port_id"`
+	PortID string `json:"port_id"`
 
 	// The UUID of the interface.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Owner of the interface.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // InterfaceResult represents the result of interface operations, such as
@@ -160,12 +147,7 @@
 
 // Extract is a function that accepts a result and extracts an information struct.
 func (r InterfaceResult) Extract() (*InterfaceInfo, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res *InterfaceInfo
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res, err
+	var s InterfaceInfo
+	err := r.ExtractInto(&s)
+	return &s, err
 }
diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go
index b948706..ae9f341 100644
--- a/openstack/networking/v2/extensions/lbaas/members/results.go
+++ b/openstack/networking/v2/extensions/lbaas/members/results.go
@@ -1,7 +1,6 @@
 package members
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -15,20 +14,20 @@
 	Weight int
 
 	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Owner of the member. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The pool to which the member belongs.
-	PoolID string `json:"pool_id" mapstructure:"pool_id"`
+	PoolID string `json:"pool_id"`
 
 	// The IP address of the member.
 	Address string
 
 	// The port on which the application is hosted.
-	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
+	ProtocolPort int `json:"protocol_port"`
 
 	// The unique ID for the member.
 	ID string
@@ -43,43 +42,33 @@
 // NextPageURL is invoked when a paginated collection of members has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MemberPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"members_links"`
+func (page MemberPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"members_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a MemberPage struct is empty.
-func (p MemberPage) IsEmpty() (bool, error) {
-	is, err := ExtractMembers(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page MemberPage) IsEmpty() (bool, error) {
+	is, err := ExtractMembers(page)
+	return len(is) == 0, err
 }
 
 // ExtractMembers accepts a Page struct, specifically a MemberPage struct,
 // and extracts the elements into a slice of Member structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractMembers(page pagination.Page) ([]Member, error) {
-	var resp struct {
-		Members []Member `mapstructure:"members" json:"members"`
+	r := page.(MemberPage)
+	var s struct {
+		Members []Member `json:"members"`
 	}
-
-	err := mapstructure.Decode(page.(MemberPage).Body, &resp)
-	if err != nil {
-		return nil, err
-	}
-
-	return resp.Members, nil
+	err := r.ExtractInto(&s)
+	return s.Members, err
 }
 
 type commonResult struct {
@@ -88,17 +77,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Member, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Member *Member `json:"member"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Member, err
+	err := r.ExtractInto(&s)
+	return s.Member, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go
index 5303df5..86c24bd 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go
@@ -1,7 +1,6 @@
 package monitors
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -27,7 +26,7 @@
 
 	// Owner of the VIP. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The type of probe sent by the load balancer to verify the member state,
 	// which is PING, TCP, HTTP, or HTTPS.
@@ -42,20 +41,20 @@
 
 	// Number of allowed connection failures before changing the status of the
 	// member to INACTIVE. A valid value is from 1 to 10.
-	MaxRetries int `json:"max_retries" mapstructure:"max_retries"`
+	MaxRetries int `json:"max_retries"`
 
 	// The HTTP method that the monitor uses for requests.
-	HTTPMethod string `json:"http_method" mapstructure:"http_method"`
+	HTTPMethod string `json:"http_method"`
 
 	// The HTTP path of the request sent by the monitor to test the health of a
 	// member. Must be a string beginning with a forward slash (/).
-	URLPath string `json:"url_path" mapstructure:"url_path"`
+	URLPath string `json:"url_path"`
 
 	// Expected HTTP codes for a passing HTTP(S) monitor.
-	ExpectedCodes string `json:"expected_codes" mapstructure:"expected_codes"`
+	ExpectedCodes string `json:"expected_codes"`
 
 	// The administrative state of the health monitor, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// The status of the health monitor. Indicates whether the health monitor is
 	// operational.
@@ -71,40 +70,34 @@
 // NextPageURL is invoked when a paginated collection of monitors has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MonitorPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"health_monitors_links"`
+func (page MonitorPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"health_monitors_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
 
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PoolPage struct is empty.
-func (p MonitorPage) IsEmpty() (bool, error) {
-	is, err := ExtractMonitors(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page MonitorPage) IsEmpty() (bool, error) {
+	is, err := ExtractMonitors(page)
+	return len(is) == 0, err
 }
 
 // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct,
 // and extracts the elements into a slice of Monitor structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
-	var resp struct {
-		Monitors []Monitor `mapstructure:"health_monitors" json:"health_monitors"`
+	r := page.(MonitorPage)
+	var s struct {
+		Monitors []Monitor `json:"health_monitors"`
 	}
-
-	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
-
-	return resp.Monitors, err
+	err := r.ExtractInto(&s)
+	return s.Monitors, err
 }
 
 type commonResult struct {
@@ -113,17 +106,11 @@
 
 // Extract is a function that accepts a result and extracts a monitor.
 func (r commonResult) Extract() (*Monitor, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Monitor *Monitor `json:"health_monitor"`
 	}
-
-	var res struct {
-		Monitor *Monitor `json:"health_monitor" mapstructure:"health_monitor"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Monitor, err
+	err := r.ExtractInto(&s)
+	return s.Monitor, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go
index 3308002..135046a 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/results.go
@@ -1,7 +1,6 @@
 package pools
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -18,7 +17,7 @@
 	// The load-balancer algorithm, which is round-robin, least-connections, and
 	// so on. This value, which must be supported, is dependent on the provider.
 	// Round-robin must be supported.
-	LBMethod string `json:"lb_method" mapstructure:"lb_method"`
+	LBMethod string `json:"lb_method"`
 
 	// The protocol of the pool, which is TCP, HTTP, or HTTPS.
 	Protocol string
@@ -27,30 +26,30 @@
 	Description string
 
 	// The IDs of associated monitors which check the health of the pool members.
-	MonitorIDs []string `json:"health_monitors" mapstructure:"health_monitors"`
+	MonitorIDs []string `json:"health_monitors"`
 
 	// The network on which the members of the pool will be located. Only members
 	// that are on this network can be added to the pool.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// Owner of the pool. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The administrative state of the pool, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Pool name. Does not have to be unique.
 	Name string
 
 	// List of member IDs that belong to the pool.
-	MemberIDs []string `json:"members" mapstructure:"members"`
+	MemberIDs []string `json:"members"`
 
 	// The unique ID for the pool.
 	ID string
 
 	// The ID of the virtual IP associated with this pool
-	VIPID string `json:"vip_id" mapstructure:"vip_id"`
+	VIPID string `json:"vip_id"`
 
 	// The provider
 	Provider string
@@ -65,40 +64,33 @@
 // NextPageURL is invoked when a paginated collection of pools has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p PoolPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"pools_links"`
+func (page PoolPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"pools_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PoolPage struct is empty.
-func (p PoolPage) IsEmpty() (bool, error) {
-	is, err := ExtractPools(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page PoolPage) IsEmpty() (bool, error) {
+	is, err := ExtractPools(page)
+	return len(is) == 0, err
 }
 
 // ExtractPools accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractPools(page pagination.Page) ([]Pool, error) {
-	var resp struct {
-		Pools []Pool `mapstructure:"pools" json:"pools"`
+	r := page.(PoolPage)
+	var s struct {
+		Pools []Pool `json:"pools"`
 	}
-
-	err := mapstructure.Decode(page.(PoolPage).Body, &resp)
-
-	return resp.Pools, err
+	err := r.ExtractInto(&s)
+	return s.Pools, err
 }
 
 type commonResult struct {
@@ -107,17 +99,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Pool, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Pool *Pool `json:"pool"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Pool, err
+	err := r.ExtractInto(&s)
+	return s.Pool, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go
index d7322ca..398c981 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/results.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/results.go
@@ -1,7 +1,6 @@
 package vips
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -23,10 +22,10 @@
 //              same member of the pool.
 type SessionPersistence struct {
 	// The type of persistence mode
-	Type string `mapstructure:"type" json:"type"`
+	Type string `json:"type"`
 
 	// Name of cookie if persistence mode is set appropriately
-	CookieName string `mapstructure:"cookie_name" json:"cookie_name,omitempty"`
+	CookieName string `json:"cookie_name,omitempty"`
 }
 
 // VirtualIP is the primary load balancing configuration object that specifies
@@ -36,49 +35,49 @@
 // server", a "vserver" or a "listener".
 type VirtualIP struct {
 	// The unique ID for the VIP.
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Owner of the VIP. Only an admin user can specify a tenant ID other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Human-readable name for the VIP. Does not have to be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// Human-readable description for the VIP.
-	Description string `mapstructure:"description" json:"description"`
+	Description string `json:"description"`
 
 	// The ID of the subnet on which to allocate the VIP address.
-	SubnetID string `mapstructure:"subnet_id" json:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// The IP address of the VIP.
-	Address string `mapstructure:"address" json:"address"`
+	Address string `json:"address"`
 
 	// The protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS.
-	Protocol string `mapstructure:"protocol" json:"protocol"`
+	Protocol string `json:"protocol"`
 
 	// The port on which to listen to client traffic that is associated with the
 	// VIP address. A valid value is from 0 to 65535.
-	ProtocolPort int `mapstructure:"protocol_port" json:"protocol_port"`
+	ProtocolPort int `json:"protocol_port"`
 
 	// The ID of the pool with which the VIP is associated.
-	PoolID string `mapstructure:"pool_id" json:"pool_id"`
+	PoolID string `json:"pool_id"`
 
 	// The ID of the port which belongs to the load balancer
-	PortID string `mapstructure:"port_id" json:"port_id"`
+	PortID string `json:"port_id"`
 
 	// Indicates whether connections in the same session will be processed by the
 	// same pool member or not.
-	Persistence SessionPersistence `mapstructure:"session_persistence" json:"session_persistence"`
+	Persistence SessionPersistence `json:"session_persistence"`
 
 	// The maximum number of connections allowed for the VIP. Default is -1,
 	// meaning no limit.
-	ConnLimit int `mapstructure:"connection_limit" json:"connection_limit"`
+	ConnLimit int `json:"connection_limit"`
 
 	// The administrative state of the VIP. A valid value is true (UP) or false (DOWN).
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// The status of the VIP. Indicates whether the VIP is operational.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 }
 
 // VIPPage is the page returned by a pager when traversing over a
@@ -90,40 +89,33 @@
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p VIPPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"vips_links"`
+func (page VIPPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"vips_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
-// IsEmpty checks whether a RouterPage struct is empty.
-func (p VIPPage) IsEmpty() (bool, error) {
-	is, err := ExtractVIPs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+// IsEmpty checks whether a VIPPage struct is empty.
+func (page VIPPage) IsEmpty() (bool, error) {
+	is, err := ExtractVIPs(page)
+	return len(is) == 0, err
 }
 
 // ExtractVIPs accepts a Page struct, specifically a VIPPage struct,
 // and extracts the elements into a slice of VirtualIP structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractVIPs(page pagination.Page) ([]VirtualIP, error) {
-	var resp struct {
-		VIPs []VirtualIP `mapstructure:"vips" json:"vips"`
+	r := page.(VIPPage)
+	var s struct {
+		VIPs []VirtualIP `json:"vips"`
 	}
-
-	err := mapstructure.Decode(page.(VIPPage).Body, &resp)
-
-	return resp.VIPs, err
+	err := r.ExtractInto(&s)
+	return s.VIPs, err
 }
 
 type commonResult struct {
@@ -132,17 +124,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*VirtualIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		VirtualIP *VirtualIP `json:"vip" json:"vip"`
 	}
-
-	var res struct {
-		VirtualIP *VirtualIP `mapstructure:"vip" json:"vip"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.VirtualIP, err
+	err := r.ExtractInto(&s)
+	return s.VirtualIP, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go
index 9590ae5..f437461 100755
--- a/openstack/networking/v2/extensions/provider/results.go
+++ b/openstack/networking/v2/extensions/provider/results.go
@@ -1,7 +1,6 @@
 package provider
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -22,30 +21,30 @@
 // NetworkExtAttrs represents an extended form of a Network with additional fields.
 type NetworkExtAttrs struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 
 	// Specifies the nature of the physical network mapped to this network
 	// resource. Examples are flat, vlan, or gre.
-	NetworkType string `json:"provider:network_type" mapstructure:"provider:network_type"`
+	NetworkType string `json:"provider:network_type"`
 
 	// Identifies the physical network on top of which this network object is
 	// being implemented. The OpenStack Networking API does not expose any facility
@@ -53,72 +52,53 @@
 	// the Open vSwitch plug-in this is a symbolic name which is then mapped to
 	// specific bridges on each compute host through the Open vSwitch plug-in
 	// configuration file.
-	PhysicalNetwork string `json:"provider:physical_network" mapstructure:"provider:physical_network"`
+	PhysicalNetwork string `json:"provider:physical_network"`
 
 	// Identifies an isolated segment on the physical network; the nature of the
 	// segment depends on the segmentation model defined by network_type. For
 	// instance, if network_type is vlan, then this is a vlan identifier;
 	// otherwise, if network_type is gre, then this will be a gre key.
-	SegmentationID string `json:"provider:segmentation_id" mapstructure:"provider:segmentation_id"`
+	SegmentationID string `json:"provider:segmentation_id"`
 }
 
 // ExtractGet decorates a GetResult struct returned from a networks.Get()
 // function with extended attributes.
 func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractCreate decorates a CreateResult struct returned from a networks.Create()
 // function with extended attributes.
 func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractUpdate decorates a UpdateResult struct returned from a
 // networks.Update() function with extended attributes.
 func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractList accepts a Page struct, specifically a NetworkPage struct, and
 // extracts the elements into a slice of NetworkExtAttrs structs. In other
 // words, a generic collection is mapped into a relevant slice.
 func ExtractList(page pagination.Page) ([]NetworkExtAttrs, error) {
-	var resp struct {
-		Networks []NetworkExtAttrs `mapstructure:"networks" json:"networks"`
+	r := page.(networks.NetworkPage)
+	var s struct {
+		Networks []NetworkExtAttrs `json:"networks" json:"networks"`
 	}
-
-	err := mapstructure.WeakDecode(page.(networks.NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := r.ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/extensions/provider/results_test.go b/openstack/networking/v2/extensions/provider/results_test.go
index 90d6ad1..59f06cc 100644
--- a/openstack/networking/v2/extensions/provider/results_test.go
+++ b/openstack/networking/v2/extensions/provider/results_test.go
@@ -49,7 +49,7 @@
             "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
             "shared": true,
             "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-            "provider:segmentation_id": 1234567890,
+            "provider:segmentation_id": "1234567890",
             "provider:physical_network": null,
             "provider:network_type": "local"
         }
diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go
index 9530cde..7479e19 100644
--- a/openstack/networking/v2/extensions/security/groups/results.go
+++ b/openstack/networking/v2/extensions/security/groups/results.go
@@ -1,7 +1,6 @@
 package groups
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
 	"github.com/gophercloud/gophercloud/pagination"
@@ -21,11 +20,11 @@
 
 	// A slice of security group rules that dictate the permitted behaviour for
 	// traffic entering and leaving the group.
-	Rules []rules.SecGroupRule `json:"security_group_rules" mapstructure:"security_group_rules"`
+	Rules []rules.SecGroupRule `json:"security_group_rules"`
 
 	// Owner of the security group. Only admin users can specify a TenantID
 	// other than their own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SecGroupPage is the page returned by a pager when traversing over a
@@ -37,40 +36,34 @@
 // NextPageURL is invoked when a paginated collection of security groups has
 // reached the end of a page and the pager seeks to traverse over a new one. In
 // order to do this, it needs to construct the next page's URL.
-func (p SecGroupPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"security_groups_links"`
+func (page SecGroupPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"security_groups_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
 
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SecGroupPage struct is empty.
-func (p SecGroupPage) IsEmpty() (bool, error) {
-	is, err := ExtractGroups(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page SecGroupPage) IsEmpty() (bool, error) {
+	is, err := ExtractGroups(page)
+	return len(is) == 0, err
 }
 
 // ExtractGroups accepts a Page struct, specifically a SecGroupPage struct,
 // and extracts the elements into a slice of SecGroup structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractGroups(page pagination.Page) ([]SecGroup, error) {
-	var resp struct {
-		SecGroups []SecGroup `mapstructure:"security_groups" json:"security_groups"`
+	r := page.(SecGroupPage)
+	var s struct {
+		SecGroups []SecGroup `json:"security_groups"`
 	}
-
-	err := mapstructure.Decode(page.(SecGroupPage).Body, &resp)
-
-	return resp.SecGroups, err
+	err := r.ExtractInto(&s)
+	return s.SecGroups, err
 }
 
 type commonResult struct {
@@ -79,17 +72,11 @@
 
 // Extract is a function that accepts a result and extracts a security group.
 func (r commonResult) Extract() (*SecGroup, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		SecGroup *SecGroup `json:"security_group"`
 	}
-
-	var res struct {
-		SecGroup *SecGroup `mapstructure:"security_group" json:"security_group"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.SecGroup, err
+	err := r.ExtractInto(&s)
+	return s.SecGroup, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go
index 1181ebb..a9d6f1b 100644
--- a/openstack/networking/v2/extensions/security/rules/results.go
+++ b/openstack/networking/v2/extensions/security/rules/results.go
@@ -1,7 +1,6 @@
 package rules
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -20,21 +19,21 @@
 
 	// Must be IPv4 or IPv6, and addresses represented in CIDR must match the
 	// ingress or egress rules.
-	EtherType string `json:"ethertype" mapstructure:"ethertype"`
+	EtherType string `json:"ethertype"`
 
 	// The security group ID to associate with this security group rule.
-	SecGroupID string `json:"security_group_id" mapstructure:"security_group_id"`
+	SecGroupID string `json:"security_group_id"`
 
 	// The minimum port number in the range that is matched by the security group
 	// rule. If the protocol is TCP or UDP, this value must be less than or equal
 	// to the value of the PortRangeMax attribute. If the protocol is ICMP, this
 	// value must be an ICMP type.
-	PortRangeMin int `json:"port_range_min" mapstructure:"port_range_min"`
+	PortRangeMin int `json:"port_range_min"`
 
 	// The maximum port number in the range that is matched by the security group
 	// rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If
 	// the protocol is ICMP, this value must be an ICMP type.
-	PortRangeMax int `json:"port_range_max" mapstructure:"port_range_max"`
+	PortRangeMax int `json:"port_range_max"`
 
 	// The protocol that is matched by the security group rule. Valid values are
 	// "tcp", "udp", "icmp" or an empty string.
@@ -42,15 +41,15 @@
 
 	// The remote group ID to be associated with this security group rule. You
 	// can specify either RemoteGroupID or RemoteIPPrefix.
-	RemoteGroupID string `json:"remote_group_id" mapstructure:"remote_group_id"`
+	RemoteGroupID string `json:"remote_group_id"`
 
 	// The remote IP prefix to be associated with this security group rule. You
 	// can specify either RemoteGroupID or RemoteIPPrefix . This attribute
 	// matches the specified IP prefix as the source IP address of the IP packet.
-	RemoteIPPrefix string `json:"remote_ip_prefix" mapstructure:"remote_ip_prefix"`
+	RemoteIPPrefix string `json:"remote_ip_prefix"`
 
 	// The owner of this security group rule.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SecGroupRulePage is the page returned by a pager when traversing over a
@@ -62,40 +61,33 @@
 // NextPageURL is invoked when a paginated collection of security group rules has
 // reached the end of a page and the pager seeks to traverse over a new one. In
 // order to do this, it needs to construct the next page's URL.
-func (p SecGroupRulePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"security_group_rules_links"`
+func (page SecGroupRulePage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"security_group_rules_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SecGroupRulePage struct is empty.
-func (p SecGroupRulePage) IsEmpty() (bool, error) {
-	is, err := ExtractRules(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page SecGroupRulePage) IsEmpty() (bool, error) {
+	is, err := ExtractRules(page)
+	return len(is) == 0, err
 }
 
 // ExtractRules accepts a Page struct, specifically a SecGroupRulePage struct,
 // and extracts the elements into a slice of SecGroupRule structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractRules(page pagination.Page) ([]SecGroupRule, error) {
-	var resp struct {
-		SecGroupRules []SecGroupRule `mapstructure:"security_group_rules" json:"security_group_rules"`
+	r := page.(SecGroupRulePage)
+	var s struct {
+		SecGroupRules []SecGroupRule `json:"security_group_rules"`
 	}
-
-	err := mapstructure.Decode(page.(SecGroupRulePage).Body, &resp)
-
-	return resp.SecGroupRules, err
+	err := r.ExtractInto(&s)
+	return s.SecGroupRules, err
 }
 
 type commonResult struct {
@@ -104,17 +96,11 @@
 
 // Extract is a function that accepts a result and extracts a security rule.
 func (r commonResult) Extract() (*SecGroupRule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		SecGroupRule *SecGroupRule `json:"security_group_rule"`
 	}
-
-	var res struct {
-		SecGroupRule *SecGroupRule `mapstructure:"security_group_rule" json:"security_group_rule"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.SecGroupRule, err
+	err := r.ExtractInto(&s)
+	return s.SecGroupRule, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go
index 7251273..7f64ddb 100644
--- a/openstack/networking/v2/networks/results.go
+++ b/openstack/networking/v2/networks/results.go
@@ -1,7 +1,6 @@
 package networks
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -12,17 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a network resource.
 func (r commonResult) Extract() (*Network, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *Network `json:"network"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -48,26 +41,26 @@
 // Network represents, well, a network.
 type Network struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 }
 
 // NetworkPage is the page returned by a pager when traversing over a
@@ -79,38 +72,31 @@
 // NextPageURL is invoked when a paginated collection of networks has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p NetworkPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"networks_links"`
+func (page NetworkPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"networks_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a NetworkPage struct is empty.
-func (p NetworkPage) IsEmpty() (bool, error) {
-	is, err := ExtractNetworks(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page NetworkPage) IsEmpty() (bool, error) {
+	is, err := ExtractNetworks(page)
+	return len(is) == 0, err
 }
 
 // ExtractNetworks accepts a Page struct, specifically a NetworkPage struct,
 // and extracts the elements into a slice of Network structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractNetworks(page pagination.Page) ([]Network, error) {
-	var resp struct {
-		Networks []Network `mapstructure:"networks" json:"networks"`
+	r := page.(NetworkPage)
+	var s struct {
+		Networks []Network `json:"networks"`
 	}
-
-	err := mapstructure.Decode(page.(NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := r.ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go
index 0908fda..e757a54 100644
--- a/openstack/networking/v2/ports/results.go
+++ b/openstack/networking/v2/ports/results.go
@@ -1,7 +1,6 @@
 package ports
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -12,16 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a port resource.
 func (r commonResult) Extract() (*Port, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Port *Port `json:"port"`
 	}
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Port, err
+	err := r.ExtractInto(&s)
+	return s.Port, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -46,44 +40,45 @@
 
 // IP is a sub-struct that represents an individual IP.
 type IP struct {
-	SubnetID  string `mapstructure:"subnet_id" json:"subnet_id"`
-	IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
+	SubnetID  string `json:"subnet_id"`
+	IPAddress string `json:"ip_address,omitempty"`
 }
 
+// AddressPair contains the IP Address and the MAC address.
 type AddressPair struct {
-	IPAddress  string `mapstructure:"ip_address" json:"ip_address,omitempty"`
-	MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
+	IPAddress  string `json:"ip_address,omitempty"`
+	MACAddress string `json:"mac_address,omitempty"`
 }
 
 // Port represents a Neutron port. See package documentation for a top-level
 // description of what this is.
 type Port struct {
 	// UUID for the port.
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 	// Network that this port is associated with.
-	NetworkID string `mapstructure:"network_id" json:"network_id"`
+	NetworkID string `json:"network_id"`
 	// Human-readable name for the port. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 	// Administrative state of port. If false (down), port does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 	// Mac address to use on this port.
-	MACAddress string `mapstructure:"mac_address" json:"mac_address"`
+	MACAddress string `json:"mac_address"`
 	// Specifies IP addresses for the port thus associating the port itself with
 	// the subnets where the IP addresses are picked from
-	FixedIPs []IP `mapstructure:"fixed_ips" json:"fixed_ips"`
+	FixedIPs []IP `json:"fixed_ips"`
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 	// Identifies the entity (e.g.: dhcp agent) using this port.
-	DeviceOwner string `mapstructure:"device_owner" json:"device_owner"`
+	DeviceOwner string `json:"device_owner"`
 	// Specifies the IDs of any security groups associated with a port.
-	SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
+	SecurityGroups []string `json:"security_groups"`
 	// Identifies the device (e.g., virtual server) using this port.
-	DeviceID string `mapstructure:"device_id" json:"device_id"`
+	DeviceID string `json:"device_id"`
 	// Identifies the list of IP addresses the port will recognize/accept
-	AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"`
 }
 
 // PortPage is the page returned by a pager when traversing over a collection
@@ -95,38 +90,31 @@
 // NextPageURL is invoked when a paginated collection of ports has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p PortPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"ports_links"`
+func (page PortPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"ports_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PortPage struct is empty.
-func (p PortPage) IsEmpty() (bool, error) {
-	is, err := ExtractPorts(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page PortPage) IsEmpty() (bool, error) {
+	is, err := ExtractPorts(page)
+	return len(is) == 0, err
 }
 
 // ExtractPorts accepts a Page struct, specifically a PortPage struct,
 // and extracts the elements into a slice of Port structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractPorts(page pagination.Page) ([]Port, error) {
-	var resp struct {
-		Ports []Port `mapstructure:"ports" json:"ports"`
+	r := page.(PortPage)
+	var s struct {
+		Ports []Port `json:"ports"`
 	}
-
-	err := mapstructure.Decode(page.(PortPage).Body, &resp)
-
-	return resp.Ports, err
+	err := r.ExtractInto(&s)
+	return s.Ports, err
 }
diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go
index 2a80f6a..22cff1e 100644
--- a/openstack/networking/v2/subnets/results.go
+++ b/openstack/networking/v2/subnets/results.go
@@ -1,7 +1,6 @@
 package subnets
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -12,17 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a subnet resource.
 func (r commonResult) Extract() (*Subnet, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Subnet *Subnet `json:"subnet"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Subnet, err
+	err := r.ExtractInto(&s)
+	return s.Subnet, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -55,35 +48,35 @@
 // HostRoute represents a route that should be used by devices with IPs from
 // a subnet (not including local subnet route).
 type HostRoute struct {
-	DestinationCIDR string `mapstructure:"destination" json:"destination"`
-	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
+	DestinationCIDR string `json:"destination"`
+	NextHop         string `json:"nexthop"`
 }
 
 // Subnet represents a subnet. See package documentation for a top-level
 // description of what this is.
 type Subnet struct {
 	// UUID representing the subnet
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 	// UUID of the parent network
-	NetworkID string `mapstructure:"network_id" json:"network_id"`
+	NetworkID string `json:"network_id"`
 	// Human-readable name for the subnet. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 	// IP version, either `4' or `6'
-	IPVersion int `mapstructure:"ip_version" json:"ip_version"`
+	IPVersion int `json:"ip_version"`
 	// CIDR representing IP range for this subnet, based on IP version
-	CIDR string `mapstructure:"cidr" json:"cidr"`
+	CIDR string `json:"cidr"`
 	// Default gateway used by devices in this subnet
-	GatewayIP string `mapstructure:"gateway_ip" json:"gateway_ip"`
+	GatewayIP string `json:"gateway_ip"`
 	// DNS name servers used by hosts in this subnet.
-	DNSNameservers []string `mapstructure:"dns_nameservers" json:"dns_nameservers"`
+	DNSNameservers []string `json:"dns_nameservers"`
 	// Sub-ranges of CIDR available for dynamic allocation to ports. See AllocationPool.
-	AllocationPools []AllocationPool `mapstructure:"allocation_pools" json:"allocation_pools"`
+	AllocationPools []AllocationPool `json:"allocation_pools"`
 	// Routes that should be used by devices with IPs from this subnet (not including local subnet route).
-	HostRoutes []HostRoute `mapstructure:"host_routes" json:"host_routes"`
+	HostRoutes []HostRoute `json:"host_routes"`
 	// Specifies whether DHCP is enabled for this subnet or not.
-	EnableDHCP bool `mapstructure:"enable_dhcp" json:"enable_dhcp"`
+	EnableDHCP bool `json:"enable_dhcp"`
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SubnetPage is the page returned by a pager when traversing over a collection
@@ -95,38 +88,30 @@
 // NextPageURL is invoked when a paginated collection of subnets has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p SubnetPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"subnets_links"`
+func (page SubnetPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"subnets_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SubnetPage struct is empty.
-func (p SubnetPage) IsEmpty() (bool, error) {
-	is, err := ExtractSubnets(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (page SubnetPage) IsEmpty() (bool, error) {
+	is, err := ExtractSubnets(page)
+	return len(is) == 0, err
 }
 
 // ExtractSubnets accepts a Page struct, specifically a SubnetPage struct,
 // and extracts the elements into a slice of Subnet structs. In other words,
 // a generic collection is mapped into a relevant slice.
 func ExtractSubnets(page pagination.Page) ([]Subnet, error) {
-	var resp struct {
-		Subnets []Subnet `mapstructure:"subnets" json:"subnets"`
+	var s struct {
+		Subnets []Subnet `json:"subnets"`
 	}
-
-	err := mapstructure.Decode(page.(SubnetPage).Body, &resp)
-
-	return resp.Subnets, err
+	err := (page.(SubnetPage)).ExtractInto(&s)
+	return s.Subnets, err
 }
diff --git a/openstack/objectstorage/v1/accounts/fixtures.go b/openstack/objectstorage/v1/accounts/fixtures.go
index 66589bf..16327e8 100644
--- a/openstack/objectstorage/v1/accounts/fixtures.go
+++ b/openstack/objectstorage/v1/accounts/fixtures.go
@@ -20,6 +20,7 @@
 		w.Header().Set("X-Account-Container-Count", "2")
 		w.Header().Set("X-Account-Bytes-Used", "14")
 		w.Header().Set("X-Account-Meta-Subject", "books")
+		w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT")
 
 		w.WriteHeader(http.StatusNoContent)
 	})
@@ -33,6 +34,7 @@
 		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
 		th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts")
 
+		w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT")
 		w.WriteHeader(http.StatusNoContent)
 	})
 }
diff --git a/openstack/objectstorage/v1/accounts/requests_test.go b/openstack/objectstorage/v1/accounts/requests_test.go
index 31ebccc..8aba591 100644
--- a/openstack/objectstorage/v1/accounts/requests_test.go
+++ b/openstack/objectstorage/v1/accounts/requests_test.go
@@ -13,8 +13,8 @@
 	HandleUpdateAccountSuccessfully(t)
 
 	options := &UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
-	res := Update(fake.ServiceClient(), options)
-	th.AssertNoErr(t, res.Err)
+	_, err := Update(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
 }
 
 func TestGetAccount(t *testing.T) {
@@ -27,6 +27,6 @@
 	th.AssertNoErr(t, res.Err)
 	actualMetadata, _ := res.ExtractMetadata()
 	th.CheckDeepEquals(t, expectedMetadata, actualMetadata)
-	//headers, err := res.Extract()
-	//th.AssertNoErr(t, err)
+	_, err := res.Extract()
+	th.AssertNoErr(t, err)
 }
diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go
index 89cd905..50032bd 100644
--- a/openstack/objectstorage/v1/accounts/results.go
+++ b/openstack/objectstorage/v1/accounts/results.go
@@ -2,7 +2,6 @@
 
 import (
 	"strings"
-	"time"
 
 	"github.com/gophercloud/gophercloud"
 )
@@ -14,46 +13,31 @@
 
 // UpdateHeader represents the headers returned in the response from an Update request.
 type UpdateHeader struct {
-	ContentLength string    `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (ur UpdateResult) Extract() (*UpdateHeader, error) {
+	var uh *UpdateHeader
+	err := ur.ExtractInto(&uh)
+	return uh, err
 }
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	BytesUsed      int64     `mapstructure:"X-Account-Bytes-Used"`
-	ContainerCount int       `mapstructure:"X-Account-Container-Count"`
-	ContentLength  int64     `mapstructure:"Content-Length"`
-	ContentType    string    `mapstructure:"Content-Type"`
-	Date           time.Time `mapstructure:"-"`
-	ObjectCount    int64     `mapstructure:"X-Account-Object-Count"`
-	TransID        string    `mapstructure:"X-Trans-Id"`
-	TempURLKey     string    `mapstructure:"X-Account-Meta-Temp-URL-Key"`
-	TempURLKey2    string    `mapstructure:"X-Account-Meta-Temp-URL-Key-2"`
+	BytesUsed      string                  `json:"X-Account-Bytes-Used"`
+	ContainerCount string                  `json:"X-Account-Container-Count"`
+	ContentLength  string                  `json:"Content-Length"`
+	ContentType    string                  `json:"Content-Type"`
+	Date           gophercloud.JSONRFC1123 `json:"Date"`
+	ObjectCount    string                  `json:"X-Account-Object-Count"`
+	TransID        string                  `json:"X-Trans-Id"`
+	TempURLKey     string                  `json:"X-Account-Meta-Temp-URL-Key"`
+	TempURLKey2    string                  `json:"X-Account-Meta-Temp-URL-Key-2"`
 }
 
 // GetResult is returned from a call to the Get function.
@@ -63,25 +47,10 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	return gh, nil
+func (gr GetResult) Extract() (*GetHeader, error) {
+	var gh *GetHeader
+	err := gr.ExtractInto(&gh)
+	return gh, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go
index 4413c74..9eec3f4 100644
--- a/openstack/objectstorage/v1/containers/results.go
+++ b/openstack/objectstorage/v1/containers/results.go
@@ -3,24 +3,21 @@
 import (
 	"fmt"
 	"strings"
-	"time"
 
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
 )
 
 // Container represents a container resource.
 type Container struct {
 	// The total number of bytes stored in the container.
-	Bytes int `json:"bytes" mapstructure:"bytes"`
+	Bytes int `json:"bytes"`
 
 	// The total number of objects stored in the container.
-	Count int `json:"count" mapstructure:"count"`
+	Count int `json:"count"`
 
 	// The name of the container.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 }
 
 // ContainerPage is the page returned by a pager when traversing over a
@@ -29,13 +26,10 @@
 	pagination.MarkerPageBase
 }
 
-// IsEmpty returns true if a ListResult contains no container names.
+//IsEmpty returns true if a ListResult contains no container names.
 func (r ContainerPage) IsEmpty() (bool, error) {
 	names, err := ExtractNames(r)
-	if err != nil {
-		return true, err
-	}
-	return len(names) == 0, nil
+	return len(names) == 0, err
 }
 
 // LastMarker returns the last container name in a ListResult.
@@ -51,17 +45,10 @@
 }
 
 // ExtractInfo is a function that takes a ListResult and returns the containers' information.
-func ExtractInfo(page pagination.Page) ([]Container, error) {
-	untyped := page.(ContainerPage).Body.([]interface{})
-	results := make([]Container, len(untyped))
-	for index, each := range untyped {
-		container := each.(map[string]interface{})
-		err := mapstructure.Decode(container, &results[index])
-		if err != nil {
-			return results, err
-		}
-	}
-	return results, nil
+func ExtractInfo(r pagination.Page) ([]Container, error) {
+	var s []Container
+	err := (r.(ContainerPage)).ExtractInto(&s)
+	return s, err
 }
 
 // ExtractNames is a function that takes a ListResult and returns the containers' names.
@@ -99,16 +86,16 @@
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	AcceptRanges     string    `mapstructure:"Accept-Ranges"`
-	BytesUsed        int64     `mapstructure:"X-Account-Bytes-Used"`
-	ContentLength    int64     `mapstructure:"Content-Length"`
-	ContentType      string    `mapstructure:"Content-Type"`
-	Date             time.Time `mapstructure:"-"`
-	ObjectCount      int64     `mapstructure:"X-Container-Object-Count"`
-	Read             string    `mapstructure:"X-Container-Read"`
-	TransID          string    `mapstructure:"X-Trans-Id"`
-	VersionsLocation string    `mapstructure:"X-Versions-Location"`
-	Write            string    `mapstructure:"X-Container-Write"`
+	AcceptRanges     string                  `json:"Accept-Ranges"`
+	BytesUsed        string                  `json:"X-Account-Bytes-Used"`
+	ContentLength    string                  `json:"Content-Length"`
+	ContentType      string                  `json:"Content-Type"`
+	Date             gophercloud.JSONRFC1123 `json:"Date"`
+	ObjectCount      string                  `json:"X-Container-Object-Count"`
+	Read             string                  `json:"X-Container-Read"`
+	TransID          string                  `json:"X-Trans-Id"`
+	VersionsLocation string                  `json:"X-Versions-Location"`
+	Write            string                  `json:"X-Container-Write"`
 }
 
 // GetResult represents the result of a get operation.
@@ -118,35 +105,20 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	return gh, nil
+func (r GetResult) Extract() (*GetHeader, error) {
+	var s *GetHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
 // and returns the custom metadata associated with the container.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) ExtractMetadata() (map[string]string, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
 	metadata := make(map[string]string)
-	for k, v := range gr.Header {
+	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Container-Meta-") {
 			key := strings.TrimPrefix(k, "X-Container-Meta-")
 			metadata[key] = v[0]
@@ -157,10 +129,10 @@
 
 // CreateHeader represents the headers returned in the response from a Create request.
 type CreateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // CreateResult represents the result of a create operation. To extract the
@@ -172,33 +144,18 @@
 
 // Extract will return a struct of headers returned from a call to Create. To obtain
 // a map of headers, call the ExtractHeader method on the CreateResult.
-func (cr CreateResult) Extract() (CreateHeader, error) {
-	var ch CreateHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	return ch, nil
+func (r CreateResult) Extract() (*CreateHeader, error) {
+	var s *CreateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // UpdateHeader represents the headers returned in the response from a Update request.
 type UpdateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // UpdateResult represents the result of an update operation. To extract the
@@ -210,33 +167,18 @@
 
 // Extract will return a struct of headers returned from a call to Update. To obtain
 // a map of headers, call the ExtractHeader method on the UpdateResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (r UpdateResult) Extract() (*UpdateHeader, error) {
+	var s *UpdateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // DeleteHeader represents the headers returned in the response from a Delete request.
 type DeleteHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // DeleteResult represents the result of a delete operation. To extract the
@@ -248,23 +190,8 @@
 
 // Extract will return a struct of headers returned from a call to Delete. To obtain
 // a map of headers, call the ExtractHeader method on the DeleteResult.
-func (dr DeleteResult) Extract() (DeleteHeader, error) {
-	var dh DeleteHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	return dh, nil
+func (r DeleteResult) Extract() (*DeleteHeader, error) {
+	var s *DeleteHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go
index 530b072..c0d532b 100644
--- a/openstack/objectstorage/v1/objects/results.go
+++ b/openstack/objectstorage/v1/objects/results.go
@@ -4,34 +4,30 @@
 	"fmt"
 	"io"
 	"io/ioutil"
-	"strconv"
 	"strings"
-	"time"
 
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
 )
 
 // Object is a structure that holds information related to a storage object.
 type Object struct {
 	// Bytes is the total number of bytes that comprise the object.
-	Bytes int64 `json:"bytes" mapstructure:"bytes"`
+	Bytes int64 `json:"bytes"`
 
 	// ContentType is the content type of the object.
-	ContentType string `json:"content_type" mapstructure:"content_type"`
+	ContentType string `json:"content_type"`
 
 	// Hash represents the MD5 checksum value of the object's content.
-	Hash string `json:"hash" mapstructure:"hash"`
+	Hash string `json:"hash"`
 
 	// LastModified is the RFC3339Milli time the object was last modified, represented
 	// as a string. For any given object (obj), this value may be parsed to a time.Time:
 	// lastModified, err := time.Parse(gophercloud.RFC3339Milli, obj.LastModified)
-	LastModified string `json:"last_modified" mapstructure:"last_modified"`
+	LastModified string `json:"last_modified"`
 
 	// Name is the unique name for the object.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 }
 
 // ObjectPage is a single page of objects that is returned from a call to the
@@ -43,10 +39,7 @@
 // IsEmpty returns true if a ListResult contains no object names.
 func (r ObjectPage) IsEmpty() (bool, error) {
 	names, err := ExtractNames(r)
-	if err != nil {
-		return true, err
-	}
-	return len(names) == 0, nil
+	return len(names) == 0, err
 }
 
 // LastMarker returns the last object name in a ListResult.
@@ -62,26 +55,19 @@
 }
 
 // ExtractInfo is a function that takes a page of objects and returns their full information.
-func ExtractInfo(page pagination.Page) ([]Object, error) {
-	untyped := page.(ObjectPage).Body.([]interface{})
-	results := make([]Object, len(untyped))
-	for index, each := range untyped {
-		object := each.(map[string]interface{})
-		err := mapstructure.Decode(object, &results[index])
-		if err != nil {
-			return results, err
-		}
-	}
-	return results, nil
+func ExtractInfo(r pagination.Page) ([]Object, error) {
+	var s []Object
+	err := (r.(ObjectPage)).ExtractInto(&s)
+	return s, err
 }
 
 // ExtractNames is a function that takes a page of objects and returns only their names.
-func ExtractNames(page pagination.Page) ([]string, error) {
-	casted := page.(ObjectPage)
+func ExtractNames(r pagination.Page) ([]string, error) {
+	casted := r.(ObjectPage)
 	ct := casted.Header.Get("Content-Type")
 	switch {
 	case strings.HasPrefix(ct, "application/json"):
-		parsed, err := ExtractInfo(page)
+		parsed, err := ExtractInfo(r)
 		if err != nil {
 			return nil, err
 		}
@@ -95,7 +81,7 @@
 	case strings.HasPrefix(ct, "text/plain"):
 		names := make([]string, 0, 50)
 
-		body := string(page.(ObjectPage).Body.([]uint8))
+		body := string(r.(ObjectPage).Body.([]uint8))
 		for _, name := range strings.Split(body, "\n") {
 			if len(name) > 0 {
 				names = append(names, name)
@@ -112,18 +98,18 @@
 
 // DownloadHeader represents the headers returned in the response from a Download request.
 type DownloadHeader struct {
-	AcceptRanges       string    `mapstructure:"Accept-Ranges"`
-	ContentDisposition string    `mapstructure:"Content-Disposition"`
-	ContentEncoding    string    `mapstructure:"Content-Encoding"`
-	ContentLength      int64     `mapstructure:"Content-Length"`
-	ContentType        string    `mapstructure:"Content-Type"`
-	Date               time.Time `mapstructure:"-"`
-	DeleteAt           time.Time `mapstructure:"-"`
-	ETag               string    `mapstructure:"Etag"`
-	LastModified       time.Time `mapstructure:"-"`
-	ObjectManifest     string    `mapstructure:"X-Object-Manifest"`
-	StaticLargeObject  bool      `mapstructure:"X-Static-Large-Object"`
-	TransID            string    `mapstructure:"X-Trans-Id"`
+	AcceptRanges       string                  `json:"Accept-Ranges"`
+	ContentDisposition string                  `json:"Content-Disposition"`
+	ContentEncoding    string                  `json:"Content-Encoding"`
+	ContentLength      string                  `json:"Content-Length"`
+	ContentType        string                  `json:"Content-Type"`
+	Date               gophercloud.JSONRFC1123 `json:"Date"`
+	DeleteAt           gophercloud.JSONUnix    `json:"X-Delete-At"`
+	ETag               string                  `json:"Etag"`
+	LastModified       gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	ObjectManifest     string                  `json:"X-Object-Manifest"`
+	StaticLargeObject  bool                    `json:"X-Static-Large-Object"`
+	TransID            string                  `json:"X-Trans-Id"`
 }
 
 // DownloadResult is a *http.Response that is returned from a call to the Download function.
@@ -134,41 +120,10 @@
 
 // Extract will return a struct of headers returned from a call to Download. To obtain
 // a map of headers, call the ExtractHeader method on the DownloadResult.
-func (dr DownloadResult) Extract() (DownloadHeader, error) {
-	var dh DownloadHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, date[0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	if date, ok := dr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, date[0])
-		if err != nil {
-			return dh, err
-		}
-		dh.LastModified = t
-	}
-
-	if date, ok := dr.Header["X-Delete-At"]; ok && len(date) > 0 {
-		unix, err := strconv.ParseInt(date[0], 10, 64)
-		if err != nil {
-			return dh, err
-		}
-		dh.DeleteAt = time.Unix(unix, 0)
-	}
-
-	return dh, nil
+func (r DownloadResult) Extract() (*DownloadHeader, error) {
+	var s *DownloadHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractContent is a function that takes a DownloadResult's io.Reader body
@@ -176,31 +131,32 @@
 // the nature of io.Reader is forward-only - meaning that it can only be read
 // once and not rewound. You can recreate a reader from the output of this
 // function by using bytes.NewReader(downloadBytes)
-func (dr DownloadResult) ExtractContent() ([]byte, error) {
-	if dr.Err != nil {
-		return nil, dr.Err
+func (r *DownloadResult) ExtractContent() ([]byte, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
-	body, err := ioutil.ReadAll(dr.Body)
+	defer r.Body.Close()
+	body, err := ioutil.ReadAll(r.Body)
 	if err != nil {
 		return nil, err
 	}
-	dr.Body.Close()
+	r.Body.Close()
 	return body, nil
 }
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	ContentDisposition string    `mapstructure:"Content-Disposition"`
-	ContentEncoding    string    `mapstructure:"Content-Encoding"`
-	ContentLength      int64     `mapstructure:"Content-Length"`
-	ContentType        string    `mapstructure:"Content-Type"`
-	Date               time.Time `mapstructure:"-"`
-	DeleteAt           time.Time `mapstructure:"-"`
-	ETag               string    `mapstructure:"Etag"`
-	LastModified       time.Time `mapstructure:"-"`
-	ObjectManifest     string    `mapstructure:"X-Object-Manifest"`
-	StaticLargeObject  bool      `mapstructure:"X-Static-Large-Object"`
-	TransID            string    `mapstructure:"X-Trans-Id"`
+	ContentDisposition string                  `json:"Content-Disposition"`
+	ContentEncoding    string                  `json:"Content-Encoding"`
+	ContentLength      string                  `json:"Content-Length"`
+	ContentType        string                  `json:"Content-Type"`
+	Date               gophercloud.JSONRFC1123 `json:"Date"`
+	DeleteAt           gophercloud.JSONUnix    `json:"X-Delete-At"`
+	ETag               string                  `json:"Etag"`
+	LastModified       gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	ObjectManifest     string                  `json:"X-Object-Manifest"`
+	StaticLargeObject  bool                    `json:"X-Static-Large-Object"`
+	TransID            string                  `json:"X-Trans-Id"`
 }
 
 // GetResult is a *http.Response that is returned from a call to the Get function.
@@ -210,51 +166,20 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	if date, ok := gr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Last-Modified"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.LastModified = t
-	}
-
-	if date, ok := gr.Header["X-Delete-At"]; ok && len(date) > 0 {
-		unix, err := strconv.ParseInt(date[0], 10, 64)
-		if err != nil {
-			return gh, err
-		}
-		gh.DeleteAt = time.Unix(unix, 0)
-	}
-
-	return gh, nil
+func (r GetResult) Extract() (*GetHeader, error) {
+	var s *GetHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
 // and returns the custom metadata associated with the object.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) ExtractMetadata() (map[string]string, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
 	metadata := make(map[string]string)
-	for k, v := range gr.Header {
+	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Object-Meta-") {
 			key := strings.TrimPrefix(k, "X-Object-Meta-")
 			metadata[key] = v[0]
@@ -265,12 +190,12 @@
 
 // CreateHeader represents the headers returned in the response from a Create request.
 type CreateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	ETag          string    `mapstructure:"Etag"`
-	LastModified  time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	ETag          string                  `json:"Etag"`
+	LastModified  gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // CreateResult represents the result of a create operation.
@@ -280,41 +205,18 @@
 
 // Extract will return a struct of headers returned from a call to Create. To obtain
 // a map of headers, call the ExtractHeader method on the CreateResult.
-func (cr CreateResult) Extract() (CreateHeader, error) {
-	var ch CreateHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	if date, ok := cr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.LastModified = t
-	}
-
-	return ch, nil
+func (r CreateResult) Extract() (*CreateHeader, error) {
+	var s *CreateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // UpdateHeader represents the headers returned in the response from a Update request.
 type UpdateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // UpdateResult represents the result of an update operation.
@@ -324,33 +226,18 @@
 
 // Extract will return a struct of headers returned from a call to Update. To obtain
 // a map of headers, call the ExtractHeader method on the UpdateResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (r UpdateResult) Extract() (*UpdateHeader, error) {
+	var s *UpdateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // DeleteHeader represents the headers returned in the response from a Delete request.
 type DeleteHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // DeleteResult represents the result of a delete operation.
@@ -360,37 +247,22 @@
 
 // Extract will return a struct of headers returned from a call to Delete. To obtain
 // a map of headers, call the ExtractHeader method on the DeleteResult.
-func (dr DeleteResult) Extract() (DeleteHeader, error) {
-	var dh DeleteHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	return dh, nil
+func (r DeleteResult) Extract() (*DeleteHeader, error) {
+	var s *DeleteHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // CopyHeader represents the headers returned in the response from a Copy request.
 type CopyHeader struct {
-	ContentLength          int64     `mapstructure:"Content-Length"`
-	ContentType            string    `mapstructure:"Content-Type"`
-	CopiedFrom             string    `mapstructure:"X-Copied-From"`
-	CopiedFromLastModified time.Time `mapstructure:"-"`
-	Date                   time.Time `mapstructure:"-"`
-	ETag                   string    `mapstructure:"Etag"`
-	LastModified           time.Time `mapstructure:"-"`
-	TransID                string    `mapstructure:"X-Trans-Id"`
+	ContentLength          int64                   `json:"Content-Length"`
+	ContentType            string                  `json:"Content-Type"`
+	CopiedFrom             string                  `json:"X-Copied-From"`
+	CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"`
+	Date                   gophercloud.JSONRFC1123 `json:"Date"`
+	ETag                   string                  `json:"Etag"`
+	LastModified           gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	TransID                string                  `json:"X-Trans-Id"`
 }
 
 // CopyResult represents the result of a copy operation.
@@ -400,39 +272,8 @@
 
 // Extract will return a struct of headers returned from a call to Copy. To obtain
 // a map of headers, call the ExtractHeader method on the CopyResult.
-func (cr CopyResult) Extract() (CopyHeader, error) {
-	var ch CopyHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	if date, ok := cr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.LastModified = t
-	}
-
-	if date, ok := cr.Header["X-Copied-From-Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["X-Copied-From-Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.CopiedFromLastModified = t
-	}
-
-	return ch, nil
+func (r CopyResult) Extract() (*CopyHeader, error) {
+	var s *CopyHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/orchestration/v1/apiversions/results.go b/openstack/orchestration/v1/apiversions/results.go
index 0373ccc..a7c22a2 100644
--- a/openstack/orchestration/v1/apiversions/results.go
+++ b/openstack/orchestration/v1/apiversions/results.go
@@ -1,7 +1,6 @@
 package apiversions
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -9,9 +8,9 @@
 // APIVersion represents an API version for Neutron. It contains the status of
 // the API, and its unique ID.
 type APIVersion struct {
-	Status string             `mapstructure:"status"`
-	ID     string             `mapstructure:"id"`
-	Links  []gophercloud.Link `mapstructure:"links"`
+	Status string             `json:"status"`
+	ID     string             `json:"id"`
+	Links  []gophercloud.Link `json:"links"`
 }
 
 // APIVersionPage is the page returned by a pager when traversing over a
@@ -23,20 +22,15 @@
 // IsEmpty checks whether an APIVersionPage struct is empty.
 func (r APIVersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractAPIVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractAPIVersions takes a collection page, extracts all of the elements,
 // and returns them a slice of APIVersion structs. It is effectively a cast.
-func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
-	var resp struct {
-		Versions []APIVersion `mapstructure:"versions"`
+func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) {
+	var s struct {
+		APIVersions []APIVersion `json:"versions"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
-
-	return resp.Versions, err
+	err := (r.(APIVersionPage)).ExtractInto(&s)
+	return s.APIVersions, err
 }
diff --git a/openstack/orchestration/v1/buildinfo/results.go b/openstack/orchestration/v1/buildinfo/results.go
index fe655a3..c3d2cdb 100644
--- a/openstack/orchestration/v1/buildinfo/results.go
+++ b/openstack/orchestration/v1/buildinfo/results.go
@@ -1,19 +1,18 @@
 package buildinfo
 
 import (
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 )
 
 // Revision represents the API/Engine revision of a Heat deployment.
 type Revision struct {
-	Revision string `mapstructure:"revision"`
+	Revision string `json:"revision"`
 }
 
 // BuildInfo represents the build information for a Heat deployment.
 type BuildInfo struct {
-	API    Revision `mapstructure:"api"`
-	Engine Revision `mapstructure:"engine"`
+	API    Revision `json:"api"`
+	Engine Revision `json:"engine"`
 }
 
 // GetResult represents the result of a Get operation.
@@ -24,14 +23,7 @@
 // Extract returns a pointer to a BuildInfo object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*BuildInfo, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res BuildInfo
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *BuildInfo
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/orchestration/v1/stackevents/fixtures.go b/openstack/orchestration/v1/stackevents/fixtures.go
index b6ac274..48524e5 100644
--- a/openstack/orchestration/v1/stackevents/fixtures.go
+++ b/openstack/orchestration/v1/stackevents/fixtures.go
@@ -15,7 +15,7 @@
 var FindExpected = []Event{
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -38,7 +38,7 @@
 	},
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -132,7 +132,7 @@
 var ListExpected = []Event{
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -155,7 +155,7 @@
 	},
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -257,7 +257,7 @@
 var ListResourceEventsExpected = []Event{
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -280,7 +280,7 @@
 	},
 	Event{
 		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
 		Links: []gophercloud.Link{
 			gophercloud.Link{
 				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -381,7 +381,7 @@
 // GetExpected represents the expected object from a Get request.
 var GetExpected = &Event{
 	ResourceName: "hello_world",
-	Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
+	Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
 	Links: []gophercloud.Link{
 		gophercloud.Link{
 			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go
index ac94bb5..6c7f183 100644
--- a/openstack/orchestration/v1/stackevents/results.go
+++ b/openstack/orchestration/v1/stackevents/results.go
@@ -1,11 +1,6 @@
 package stackevents
 
 import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -13,23 +8,23 @@
 // Event represents a stack event.
 type Event struct {
 	// The name of the resource for which the event occurred.
-	ResourceName string `mapstructure:"resource_name"`
+	ResourceName string `json:"resource_name"`
 	// The time the event occurred.
-	Time time.Time `mapstructure:"-"`
+	Time gophercloud.JSONRFC3339NoZ `json:"event_time"`
 	// The URLs to the event.
-	Links []gophercloud.Link `mapstructure:"links"`
+	Links []gophercloud.Link `json:"links"`
 	// The logical ID of the stack resource.
-	LogicalResourceID string `mapstructure:"logical_resource_id"`
+	LogicalResourceID string `json:"logical_resource_id"`
 	// The reason of the status of the event.
-	ResourceStatusReason string `mapstructure:"resource_status_reason"`
+	ResourceStatusReason string `json:"resource_status_reason"`
 	// The status of the event.
-	ResourceStatus string `mapstructure:"resource_status"`
+	ResourceStatus string `json:"resource_status"`
 	// The physical ID of the stack resource.
-	PhysicalResourceID string `mapstructure:"physical_resource_id"`
+	PhysicalResourceID string `json:"physical_resource_id"`
 	// The event ID.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 	// Properties of the stack resource.
-	ResourceProperties map[string]interface{} `mapstructure:"resource_properties"`
+	ResourceProperties map[string]interface{} `json:"resource_properties"`
 }
 
 // FindResult represents the result of a Find operation.
@@ -40,32 +35,11 @@
 // Extract returns a slice of Event objects and is called after a
 // Find operation.
 func (r FindResult) Extract() ([]Event, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Events []Event `json:"events"`
 	}
-
-	var res struct {
-		Res []Event `mapstructure:"events"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	events := r.Body.(map[string]interface{})["events"].([]interface{})
-
-	for i, eventRaw := range events {
-		event := eventRaw.(map[string]interface{})
-		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].Time = t
-		}
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Events, err
 }
 
 // EventPage abstracts the raw results of making a List() request against the API.
@@ -78,10 +52,7 @@
 // IsEmpty returns true if a page contains no Server results.
 func (r EventPage) IsEmpty() (bool, error) {
 	events, err := ExtractEvents(r)
-	if err != nil {
-		return true, err
-	}
-	return len(events) == 0, nil
+	return len(events) == 0, err
 }
 
 // LastMarker returns the last stack ID in a ListResult.
@@ -97,39 +68,12 @@
 }
 
 // ExtractEvents interprets the results of a single page from a List() call, producing a slice of Event entities.
-func ExtractEvents(page pagination.Page) ([]Event, error) {
-	casted := page.(EventPage).Body
-
-	var res struct {
-		Res []Event `mapstructure:"events"`
+func ExtractEvents(r pagination.Page) ([]Event, error) {
+	var s struct {
+		Events []Event `json:"events"`
 	}
-
-	if err := mapstructure.Decode(casted, &res); err != nil {
-		return nil, err
-	}
-
-	var events []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		events = casted.(map[string]interface{})["events"].([]interface{})
-	case map[string][]interface{}:
-		events = casted.(map[string][]interface{})["events"]
-	default:
-		return res.Res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, eventRaw := range events {
-		event := eventRaw.(map[string]interface{})
-		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].Time = t
-		}
-	}
-
-	return res.Res, nil
+	err := (r.(EventPage)).ExtractInto(&s)
+	return s.Events, err
 }
 
 // ExtractResourceEvents interprets the results of a single page from a
@@ -146,27 +90,9 @@
 // Extract returns a pointer to an Event object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*Event, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Event *Event `json:"event"`
 	}
-
-	var res struct {
-		Res *Event `mapstructure:"event"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	event := r.Body.(map[string]interface{})["event"].(map[string]interface{})
-
-	if date, ok := event["event_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.Time = t
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Event, err
 }
diff --git a/openstack/orchestration/v1/stackresources/fixtures.go b/openstack/orchestration/v1/stackresources/fixtures.go
index cdf1f3c..a622f7f 100644
--- a/openstack/orchestration/v1/stackresources/fixtures.go
+++ b/openstack/orchestration/v1/stackresources/fixtures.go
@@ -27,8 +27,8 @@
 		},
 		LogicalID:    "hello_world",
 		StatusReason: "state changed",
-		UpdatedTime:  time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC)),
 		RequiredBy:   []interface{}{},
 		Status:       "CREATE_IN_PROGRESS",
 		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
@@ -98,8 +98,8 @@
 		},
 		LogicalID:    "hello_world",
 		StatusReason: "state changed",
-		UpdatedTime:  time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC)),
 		RequiredBy:   []interface{}{},
 		Status:       "CREATE_IN_PROGRESS",
 		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
@@ -176,7 +176,7 @@
 	LogicalID:    "wordpress_instance",
 	Attributes:   map[string]interface{}{"SXSW": "atx"},
 	StatusReason: "state changed",
-	UpdatedTime:  time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC),
+	UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC)),
 	RequiredBy:   []interface{}{},
 	Status:       "CREATE_COMPLETE",
 	PhysicalID:   "00e3a2fe-c65d-403c-9483-4db9930dd194",
diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go
index edbd34f..951f144 100644
--- a/openstack/orchestration/v1/stackresources/results.go
+++ b/openstack/orchestration/v1/stackresources/results.go
@@ -2,29 +2,25 @@
 
 import (
 	"encoding/json"
-	"fmt"
-	"reflect"
-	"time"
 
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Resource represents a stack resource.
 type Resource struct {
-	Attributes   map[string]interface{} `mapstructure:"attributes"`
-	CreationTime time.Time              `mapstructure:"-"`
-	Description  string                 `mapstructure:"description"`
-	Links        []gophercloud.Link     `mapstructure:"links"`
-	LogicalID    string                 `mapstructure:"logical_resource_id"`
-	Name         string                 `mapstructure:"resource_name"`
-	PhysicalID   string                 `mapstructure:"physical_resource_id"`
-	RequiredBy   []interface{}          `mapstructure:"required_by"`
-	Status       string                 `mapstructure:"resource_status"`
-	StatusReason string                 `mapstructure:"resource_status_reason"`
-	Type         string                 `mapstructure:"resource_type"`
-	UpdatedTime  time.Time              `mapstructure:"-"`
+	Attributes   map[string]interface{}     `json:"attributes"`
+	CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description  string                     `json:"description"`
+	Links        []gophercloud.Link         `json:"links"`
+	LogicalID    string                     `json:"logical_resource_id"`
+	Name         string                     `json:"resource_name"`
+	PhysicalID   string                     `json:"physical_resource_id"`
+	RequiredBy   []interface{}              `json:"required_by"`
+	Status       string                     `json:"resource_status"`
+	StatusReason string                     `json:"resource_status_reason"`
+	Type         string                     `json:"resource_type"`
+	UpdatedTime  gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // FindResult represents the result of a Find operation.
@@ -35,39 +31,11 @@
 // Extract returns a slice of Resource objects and is called after a
 // Find operation.
 func (r FindResult) Extract() ([]Resource, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Resources []Resource `json:"resources"`
 	}
-
-	var res struct {
-		Res []Resource `mapstructure:"resources"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	resources := r.Body.(map[string]interface{})["resources"].([]interface{})
-
-	for i, resourceRaw := range resources {
-		resource := resourceRaw.(map[string]interface{})
-		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].UpdatedTime = t
-		}
-		if date, ok := resource["creation_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].CreationTime = t
-		}
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Resources, err
 }
 
 // ResourcePage abstracts the raw results of making a List() request against the API.
@@ -80,51 +48,16 @@
 // IsEmpty returns true if a page contains no Server results.
 func (r ResourcePage) IsEmpty() (bool, error) {
 	resources, err := ExtractResources(r)
-	if err != nil {
-		return true, err
-	}
-	return len(resources) == 0, nil
+	return len(resources) == 0, err
 }
 
 // ExtractResources interprets the results of a single page from a List() call, producing a slice of Resource entities.
-func ExtractResources(page pagination.Page) ([]Resource, error) {
-	casted := page.(ResourcePage).Body
-
-	var response struct {
-		Resources []Resource `mapstructure:"resources"`
+func ExtractResources(r pagination.Page) ([]Resource, error) {
+	var s struct {
+		Resources []Resource `json:"resources"`
 	}
-	if err := mapstructure.Decode(casted, &response); err != nil {
-		return nil, err
-	}
-	var resources []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		resources = casted.(map[string]interface{})["resources"].([]interface{})
-	case map[string][]interface{}:
-		resources = casted.(map[string][]interface{})["resources"]
-	default:
-		return response.Resources, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, resourceRaw := range resources {
-		resource := resourceRaw.(map[string]interface{})
-		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			response.Resources[i].UpdatedTime = t
-		}
-		if date, ok := resource["creation_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			response.Resources[i].CreationTime = t
-		}
-	}
-
-	return response.Resources, nil
+	err := (r.(ResourcePage)).ExtractInto(&s)
+	return s.Resources, err
 }
 
 // GetResult represents the result of a Get operation.
@@ -135,36 +68,11 @@
 // Extract returns a pointer to a Resource object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*Resource, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Resource *Resource `json:"resource"`
 	}
-
-	var res struct {
-		Res *Resource `mapstructure:"resource"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	resource := r.Body.(map[string]interface{})["resource"].(map[string]interface{})
-
-	if date, ok := resource["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.UpdatedTime = t
-	}
-	if date, ok := resource["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.CreationTime = t
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Resource, err
 }
 
 // MetadataResult represents the result of a Metadata operation.
@@ -175,19 +83,11 @@
 // Extract returns a map object and is called after a
 // Metadata operation.
 func (r MetadataResult) Extract() (map[string]string, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Meta map[string]string `json:"metadata"`
 	}
-
-	var res struct {
-		Meta map[string]string `mapstructure:"metadata"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return res.Meta, nil
+	err := r.ExtractInto(&s)
+	return s.Meta, err
 }
 
 // ResourceTypePage abstracts the raw results of making a ListTypes() request against the API.
@@ -200,10 +100,7 @@
 // IsEmpty returns true if a ResourceTypePage contains no resource types.
 func (r ResourceTypePage) IsEmpty() (bool, error) {
 	rts, err := ExtractResourceTypes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(rts) == 0, nil
+	return len(rts) == 0, err
 }
 
 // ResourceTypes represents the type that holds the result of ExtractResourceTypes.
@@ -223,25 +120,20 @@
 }
 
 // ExtractResourceTypes extracts and returns resource types.
-func ExtractResourceTypes(page pagination.Page) (ResourceTypes, error) {
-	casted := page.(ResourceTypePage).Body
-
-	var response struct {
-		ResourceTypes ResourceTypes `mapstructure:"resource_types"`
+func ExtractResourceTypes(r pagination.Page) (ResourceTypes, error) {
+	var s struct {
+		ResourceTypes ResourceTypes `json:"resource_types"`
 	}
-
-	if err := mapstructure.Decode(casted, &response); err != nil {
-		return nil, err
-	}
-	return response.ResourceTypes, nil
+	err := (r.(ResourceTypePage)).ExtractInto(&s)
+	return s.ResourceTypes, err
 }
 
 // TypeSchema represents a stack resource schema.
 type TypeSchema struct {
-	Attributes    map[string]interface{} `mapstructure:"attributes"`
-	Properties    map[string]interface{} `mapstrucutre:"properties"`
-	ResourceType  string                 `mapstructure:"resource_type"`
-	SupportStatus map[string]interface{} `mapstructure:"support_status"`
+	Attributes    map[string]interface{} `json:"attributes"`
+	Properties    map[string]interface{} `json:"properties"`
+	ResourceType  string                 `json:"resource_type"`
+	SupportStatus map[string]interface{} `json:"support_status"`
 }
 
 // SchemaResult represents the result of a Schema operation.
@@ -252,17 +144,9 @@
 // Extract returns a pointer to a TypeSchema object and is called after a
 // Schema operation.
 func (r SchemaResult) Extract() (*TypeSchema, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res TypeSchema
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *TypeSchema
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // TemplateResult represents the result of a Template operation.
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 49e4168..f1d66f4 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -60,7 +60,7 @@
 		},
 		StatusReason: "Stack CREATE completed successfully",
 		Name:         "postman_stack",
-		CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
 		Status:       "CREATE_COMPLETE",
 		ID:           "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
 		Tags:         []string{"rackspace", "atx"},
@@ -75,8 +75,8 @@
 		},
 		StatusReason: "Stack successfully updated",
 		Name:         "gophercloud-test-stack-2",
-		CreationTime: time.Date(2014, 12, 11, 17, 39, 16, 0, time.UTC),
-		UpdatedTime:  time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 11, 17, 39, 16, 0, time.UTC)),
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC)),
 		Status:       "UPDATE_COMPLETE",
 		ID:           "db6977b2-27aa-4775-9ae7-6213212d4ada",
 		Tags:         []string{"sfo", "satx"},
@@ -157,7 +157,7 @@
 	StatusReason: "Stack CREATE completed successfully",
 	Name:         "postman_stack",
 	Outputs:      []map[string]interface{}{},
-	CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
+	CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
 	Links: []gophercloud.Link{
 		gophercloud.Link{
 			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
@@ -255,7 +255,7 @@
 		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
 	},
 	Name:         "postman_stack",
-	CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
+	CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
 	Links: []gophercloud.Link{
 		gophercloud.Link{
 			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
index 72855ff..5da178e 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -2,19 +2,15 @@
 
 import (
 	"encoding/json"
-	"fmt"
-	"reflect"
-	"time"
 
-	"github.com/mitchellh/mapstructure"
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreatedStack represents the object extracted from a Create operation.
 type CreatedStack struct {
-	ID    string             `mapstructure:"id"`
-	Links []gophercloud.Link `mapstructure:"links"`
+	ID    string             `json:"id"`
+	Links []gophercloud.Link `json:"links"`
 }
 
 // CreateResult represents the result of a Create operation.
@@ -25,19 +21,11 @@
 // Extract returns a pointer to a CreatedStack object and is called after a
 // Create operation.
 func (r CreateResult) Extract() (*CreatedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		CreatedStack *CreatedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *CreatedStack `mapstructure:"stack"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return res.Stack, nil
+	err := r.ExtractInto(&s)
+	return s.CreatedStack, err
 }
 
 // AdoptResult represents the result of an Adopt operation. AdoptResult has the
@@ -54,90 +42,50 @@
 // IsEmpty returns true if a ListResult contains no Stacks.
 func (r StackPage) IsEmpty() (bool, error) {
 	stacks, err := ExtractStacks(r)
-	if err != nil {
-		return true, err
-	}
-	return len(stacks) == 0, nil
+	return len(stacks) == 0, err
 }
 
 // ListedStack represents an element in the slice extracted from a List operation.
 type ListedStack struct {
-	CreationTime time.Time          `mapstructure:"-"`
-	Description  string             `mapstructure:"description"`
-	ID           string             `mapstructure:"id"`
-	Links        []gophercloud.Link `mapstructure:"links"`
-	Name         string             `mapstructure:"stack_name"`
-	Status       string             `mapstructure:"stack_status"`
-	StatusReason string             `mapstructure:"stack_status_reason"`
-	Tags         []string           `mapstructure:"tags"`
-	UpdatedTime  time.Time          `mapstructure:"-"`
+	CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description  string                     `json:"description"`
+	ID           string                     `json:"id"`
+	Links        []gophercloud.Link         `json:"links"`
+	Name         string                     `json:"stack_name"`
+	Status       string                     `json:"stack_status"`
+	StatusReason string                     `json:"stack_status_reason"`
+	Tags         []string                   `json:"tags"`
+	UpdatedTime  gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // ExtractStacks extracts and returns a slice of ListedStack. It is used while iterating
 // over a stacks.List call.
-func ExtractStacks(page pagination.Page) ([]ListedStack, error) {
-	casted := page.(StackPage).Body
-
-	var res struct {
-		Stacks []ListedStack `mapstructure:"stacks"`
+func ExtractStacks(r pagination.Page) ([]ListedStack, error) {
+	var s struct {
+		ListedStacks []ListedStack `json:"stacks"`
 	}
-
-	err := mapstructure.Decode(casted, &res)
-	if err != nil {
-		return nil, err
-	}
-
-	var rawStacks []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		rawStacks = casted.(map[string]interface{})["stacks"].([]interface{})
-	case map[string][]interface{}:
-		rawStacks = casted.(map[string][]interface{})["stacks"]
-	default:
-		return res.Stacks, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawStacks {
-		thisStack := (rawStacks[i]).(map[string]interface{})
-
-		if t, ok := thisStack["creation_time"].(string); ok && t != "" {
-			creationTime, err := time.Parse(gophercloud.StackFmtTime, t)
-			if err != nil {
-				return res.Stacks, err
-			}
-			res.Stacks[i].CreationTime = creationTime
-		}
-
-		if t, ok := thisStack["updated_time"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(gophercloud.StackFmtTime, t)
-			if err != nil {
-				return res.Stacks, err
-			}
-			res.Stacks[i].UpdatedTime = updatedTime
-		}
-	}
-
-	return res.Stacks, nil
+	err := (r.(StackPage)).ExtractInto(&s)
+	return s.ListedStacks, err
 }
 
 // RetrievedStack represents the object extracted from a Get operation.
 type RetrievedStack struct {
-	Capabilities        []interface{}            `mapstructure:"capabilities"`
-	CreationTime        time.Time                `mapstructure:"-"`
-	Description         string                   `mapstructure:"description"`
-	DisableRollback     bool                     `mapstructure:"disable_rollback"`
-	ID                  string                   `mapstructure:"id"`
-	Links               []gophercloud.Link       `mapstructure:"links"`
-	NotificationTopics  []interface{}            `mapstructure:"notification_topics"`
-	Outputs             []map[string]interface{} `mapstructure:"outputs"`
-	Parameters          map[string]string        `mapstructure:"parameters"`
-	Name                string                   `mapstructure:"stack_name"`
-	Status              string                   `mapstructure:"stack_status"`
-	StatusReason        string                   `mapstructure:"stack_status_reason"`
-	Tags                []string                 `mapstructure:"tags"`
-	TemplateDescription string                   `mapstructure:"template_description"`
-	Timeout             int                      `mapstructure:"timeout_mins"`
-	UpdatedTime         time.Time                `mapstructure:"-"`
+	Capabilities        []interface{}              `json:"capabilities"`
+	CreationTime        gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description         string                     `json:"description"`
+	DisableRollback     bool                       `json:"disable_rollback"`
+	ID                  string                     `json:"id"`
+	Links               []gophercloud.Link         `json:"links"`
+	NotificationTopics  []interface{}              `json:"notification_topics"`
+	Outputs             []map[string]interface{}   `json:"outputs"`
+	Parameters          map[string]string          `json:"parameters"`
+	Name                string                     `json:"stack_name"`
+	Status              string                     `json:"stack_status"`
+	StatusReason        string                     `json:"stack_status_reason"`
+	Tags                []string                   `json:"tags"`
+	TemplateDescription string                     `json:"template_description"`
+	Timeout             int                        `json:"timeout_mins"`
+	UpdatedTime         gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // GetResult represents the result of a Get operation.
@@ -148,46 +96,11 @@
 // Extract returns a pointer to a RetrievedStack object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*RetrievedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Stack *RetrievedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *RetrievedStack `mapstructure:"stack"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		Result:           &res,
-		WeaklyTypedInput: true,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := decoder.Decode(r.Body); err != nil {
-		return nil, err
-	}
-
-	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
-
-	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.CreationTime = t
-	}
-
-	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.UpdatedTime = t
-	}
-
-	return res.Stack, err
+	err := r.ExtractInto(&s)
+	return s.Stack, err
 }
 
 // UpdateResult represents the result of a Update operation.
@@ -202,19 +115,19 @@
 
 // PreviewedStack represents the result of a Preview operation.
 type PreviewedStack struct {
-	Capabilities        []interface{}      `mapstructure:"capabilities"`
-	CreationTime        time.Time          `mapstructure:"-"`
-	Description         string             `mapstructure:"description"`
-	DisableRollback     bool               `mapstructure:"disable_rollback"`
-	ID                  string             `mapstructure:"id"`
-	Links               []gophercloud.Link `mapstructure:"links"`
-	Name                string             `mapstructure:"stack_name"`
-	NotificationTopics  []interface{}      `mapstructure:"notification_topics"`
-	Parameters          map[string]string  `mapstructure:"parameters"`
-	Resources           []interface{}      `mapstructure:"resources"`
-	TemplateDescription string             `mapstructure:"template_description"`
-	Timeout             int                `mapstructure:"timeout_mins"`
-	UpdatedTime         time.Time          `mapstructure:"-"`
+	Capabilities        []interface{}              `json:"capabilities"`
+	CreationTime        gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description         string                     `json:"description"`
+	DisableRollback     bool                       `json:"disable_rollback"`
+	ID                  string                     `json:"id"`
+	Links               []gophercloud.Link         `json:"links"`
+	Name                string                     `json:"stack_name"`
+	NotificationTopics  []interface{}              `json:"notification_topics"`
+	Parameters          map[string]string          `json:"parameters"`
+	Resources           []interface{}              `json:"resources"`
+	TemplateDescription string                     `json:"template_description"`
+	Timeout             int                        `json:"timeout_mins"`
+	UpdatedTime         gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // PreviewResult represents the result of a Preview operation.
@@ -225,60 +138,25 @@
 // Extract returns a pointer to a PreviewedStack object and is called after a
 // Preview operation.
 func (r PreviewResult) Extract() (*PreviewedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		PreviewedStack *PreviewedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *PreviewedStack `mapstructure:"stack"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		Result:           &res,
-		WeaklyTypedInput: true,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := decoder.Decode(r.Body); err != nil {
-		return nil, err
-	}
-
-	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
-
-	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.CreationTime = t
-	}
-
-	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.StackFmtTime, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.UpdatedTime = t
-	}
-
-	return res.Stack, err
+	err := r.ExtractInto(&s)
+	return s.PreviewedStack, err
 }
 
 // AbandonedStack represents the result of an Abandon operation.
 type AbandonedStack struct {
-	Status             string                 `mapstructure:"status"`
-	Name               string                 `mapstructure:"name"`
-	Template           map[string]interface{} `mapstructure:"template"`
-	Action             string                 `mapstructure:"action"`
-	ID                 string                 `mapstructure:"id"`
-	Resources          map[string]interface{} `mapstructure:"resources"`
-	Files              map[string]string      `mapstructure:"files"`
-	StackUserProjectID string                 `mapstructure:"stack_user_project_id"`
-	ProjectID          string                 `mapstructure:"project_id"`
-	Environment        map[string]interface{} `mapstructure:"environment"`
+	Status             string                 `json:"status"`
+	Name               string                 `json:"name"`
+	Template           map[string]interface{} `json:"template"`
+	Action             string                 `json:"action"`
+	ID                 string                 `json:"id"`
+	Resources          map[string]interface{} `json:"resources"`
+	Files              map[string]string      `json:"files"`
+	StackUserProjectID string                 `json:"stack_user_project_id"`
+	ProjectID          string                 `json:"project_id"`
+	Environment        map[string]interface{} `json:"environment"`
 }
 
 // AbandonResult represents the result of an Abandon operation.
@@ -289,17 +167,9 @@
 // Extract returns a pointer to an AbandonedStack object and is called after an
 // Abandon operation.
 func (r AbandonResult) Extract() (*AbandonedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res AbandonedStack
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *AbandonedStack
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // String converts an AbandonResult to a string. This is useful to when passing
diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go
index 7f4ea25..bca959b 100644
--- a/openstack/orchestration/v1/stacktemplates/results.go
+++ b/openstack/orchestration/v1/stacktemplates/results.go
@@ -2,7 +2,7 @@
 
 import (
 	"encoding/json"
-	"github.com/mitchellh/mapstructure"
+
 	"github.com/gophercloud/gophercloud"
 )
 
@@ -25,9 +25,9 @@
 
 // ValidatedTemplate represents the parsed object returned from a Validate request.
 type ValidatedTemplate struct {
-	Description     string                 `mapstructure:"Description"`
-	Parameters      map[string]interface{} `mapstructure:"Parameters"`
-	ParameterGroups map[string]interface{} `mapstructure:"ParameterGroups"`
+	Description     string                 `json:"Description"`
+	Parameters      map[string]interface{} `json:"Parameters"`
+	ParameterGroups map[string]interface{} `json:"ParameterGroups"`
 }
 
 // ValidateResult represents the result of a Validate operation.
@@ -38,14 +38,7 @@
 // Extract returns a pointer to a ValidatedTemplate object and is called after a
 // Validate operation.
 func (r ValidateResult) Extract() (*ValidatedTemplate, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res ValidatedTemplate
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *ValidatedTemplate
+	err := r.ExtractInto(&s)
+	return s, err
 }