struct tags for networking v2
diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go
index da47926..f626f66 100644
--- a/openstack/networking/v2/extensions/external/requests.go
+++ b/openstack/networking/v2/extensions/external/requests.go
@@ -1,51 +1,21 @@
 package external
 
 import (
-	"time"
-
+	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
 // CreateOpts is the structure used when creating new external network
 // resources. It embeds networks.CreateOpts and so inherits all of its required
 // and optional fields, with the addition of the External field.
 type CreateOpts struct {
-	Parent   networks.CreateOpts
-	External bool
+	networks.CreateOpts
+	External *bool `json:"router:extenal,omitempty"`
 }
 
 // ToNetworkCreateMap casts a CreateOpts struct to a map.
-func (o CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
-
-	// DO NOT REMOVE. Though this line seemingly does nothing of value, it is a
-	// splint to prevent the unit test from failing on Go Tip. We suspect it is a
-	// compiler issue that will hopefully be worked out prior to our next release.
-	// Again, for all the unit tests to pass, this line is necessary and sufficient
-	// at the moment. We should reassess after the Go 1.5 release to determine
-	// if this line is still needed.
-	time.Sleep(0 * time.Millisecond)
-
-	outer, err := o.Parent.ToNetworkCreateMap()
-	if err != nil {
-		return nil, err
-	}
-
-	outer["network"].(map[string]interface{})["router:external"] = o.External
-
-	return outer, nil
+func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // UpdateOpts is the structure used when updating existing external network
@@ -53,17 +23,10 @@
 // and optional fields, with the addition of the External field.
 type UpdateOpts struct {
 	Parent   networks.UpdateOpts
-	External bool
+	External *bool `json:"router:extenal,omitempty"`
 }
 
 // ToNetworkUpdateMap casts an UpdateOpts struct to a map.
-func (o UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
-	outer, err := o.Parent.ToNetworkUpdateMap()
-	if err != nil {
-		return nil, err
-	}
-
-	outer["network"].(map[string]interface{})["router:external"] = o.External
-
-	return outer, nil
+func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "network")
 }
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
index 68cc5e9..a91710d 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
@@ -5,24 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Shared gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Yes` and `No` enums.
-type Shared *bool
-
-// Convenience vars for AdminStateUp and Shared values.
-var (
-	iTrue             = true
-	iFalse            = false
-	Up     AdminState = &iTrue
-	Down   AdminState = &iFalse
-	Yes    Shared     = &iTrue
-	No     Shared     = &iFalse
-)
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -51,10 +33,7 @@
 // ToFirewallListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToFirewallListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -65,7 +44,6 @@
 // tenant who submits the request, unless an admin user submits the request.
 func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := rootURL(c)
-
 	if opts != nil {
 		query, err := opts.ToFirewallListQuery()
 		if err != nil {
@@ -73,7 +51,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return FirewallPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -89,65 +66,38 @@
 
 // CreateOpts contains all the values needed to create a new firewall.
 type CreateOpts struct {
+	PolicyID string `json:"firewall_policy_id" required:"true"`
 	// Only required if the caller has an admin role and wants to create a firewall
 	// for another tenant.
-	TenantID     string
-	Name         string
-	Description  string
-	AdminStateUp *bool
-	Shared       *bool
-	PolicyID     string
+	TenantID     string `json:"tenant_id,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Description  string `json:"description,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
 }
 
 // ToFirewallCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
-	if opts.PolicyID == "" {
-		return nil, errPolicyRequired
-	}
-
-	f := make(map[string]interface{})
-
-	if opts.TenantID != "" {
-		f["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		f["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		f["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		f["shared"] = *opts.Shared
-	}
-	if opts.AdminStateUp != nil {
-		f["admin_state_up"] = *opts.AdminStateUp
-	}
-	if opts.PolicyID != "" {
-		f["firewall_policy_id"] = opts.PolicyID
-	}
-
-	return map[string]interface{}{"firewall": f}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToFirewallCreateMap()
+	var r CreateResult
+	b, err := opts.ToFirewallCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular firewall based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -160,57 +110,35 @@
 
 // UpdateOpts contains the values used when updating a firewall.
 type UpdateOpts struct {
-	// Name of the firewall.
-	Name         string
-	Description  string
-	AdminStateUp *bool
-	Shared       *bool
-	PolicyID     string
+	PolicyID     string `json:"firewall_policy_id" required:"true"`
+	Name         string `json:"name,omitempty"`
+	Description  string `json:"description,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
 }
 
 // ToFirewallUpdateMap casts a CreateOpts struct to a map.
 func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
-	f := make(map[string]interface{})
-
-	if opts.Name != "" {
-		f["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		f["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		f["shared"] = *opts.Shared
-	}
-	if opts.AdminStateUp != nil {
-		f["admin_state_up"] = *opts.AdminStateUp
-	}
-	if opts.PolicyID != "" {
-		f["firewall_policy_id"] = opts.PolicyID
-	}
-
-	return map[string]interface{}{"firewall": f}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall")
 }
 
 // Update allows firewalls to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToFirewallUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToFirewallUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular firewall based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go
index 2a92408..d4c3665 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go
@@ -5,18 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// Binary gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Yes` and `No` enums
-type Binary *bool
-
-// Convenience vars for Audited and Shared values.
-var (
-	iTrue         = true
-	iFalse        = false
-	Yes    Binary = &iTrue
-	No     Binary = &iFalse
-)
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -32,8 +20,8 @@
 	TenantID    string `q:"tenant_id"`
 	Name        string `q:"name"`
 	Description string `q:"description"`
-	Shared      bool   `q:"shared"`
-	Audited     bool   `q:"audited"`
+	Shared      *bool  `q:"shared"`
+	Audited     *bool  `q:"audited"`
 	ID          string `q:"id"`
 	Limit       int    `q:"limit"`
 	Marker      string `q:"marker"`
@@ -44,10 +32,7 @@
 // ToPolicyListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToPolicyListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -58,7 +43,6 @@
 // tenant who submits the request, unless an admin user submits the request.
 func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := rootURL(c)
-
 	if opts != nil {
 		query, err := opts.ToPolicyListQuery()
 		if err != nil {
@@ -66,7 +50,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PolicyPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -77,66 +60,43 @@
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type CreateOptsBuilder interface {
-	ToPolicyCreateMap() (map[string]interface{}, error)
+	ToFirewallPolicyCreateMap() (map[string]interface{}, error)
 }
 
 // CreateOpts contains all the values needed to create a new firewall policy.
 type CreateOpts struct {
 	// Only required if the caller has an admin role and wants to create a firewall policy
 	// for another tenant.
-	TenantID    string
-	Name        string
-	Description string
-	Shared      *bool
-	Audited     *bool
-	Rules       []string
+	TenantID    string   `json:"tenant_id,omitempty"`
+	Name        string   `json:"name,omitempty"`
+	Description string   `json:"description,omitempty"`
+	Shared      *bool    `json:"shared,omitempty"`
+	Audited     *bool    `json:"audited,omitempty"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
-// ToPolicyCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.TenantID != "" {
-		p["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		p["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
-	}
-	if opts.Audited != nil {
-		p["audited"] = *opts.Audited
-	}
-	if opts.Rules != nil {
-		p["firewall_rules"] = opts.Rules
-	}
-
-	return map[string]interface{}{"firewall_policy": p}, nil
+// ToFirewallPolicyCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "firewall_policy")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall policy
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPolicyCreateMap()
+	var r CreateResult
+	b, err := opts.ToFirewallPolicyCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular firewall policy based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -144,100 +104,76 @@
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type UpdateOptsBuilder interface {
-	ToPolicyUpdateMap() (map[string]interface{}, error)
+	ToFirewallPolicyUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a firewall policy.
 type UpdateOpts struct {
-	// Name of the firewall policy.
-	Name        string
-	Description string
-	Shared      *bool
-	Audited     *bool
-	Rules       []string
+	Name        string   `json:"name,omitempty"`
+	Description string   `json:"description,omitempty"`
+	Shared      *bool    `json:"shared,omitempty"`
+	Audited     *bool    `json:"audited,omitempty"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
-// ToPolicyUpdateMap casts a CreateOpts struct to a map.
-func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		p["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
-	}
-	if opts.Audited != nil {
-		p["audited"] = *opts.Audited
-	}
-	if opts.Rules != nil {
-		p["firewall_rules"] = opts.Rules
-	}
-
-	return map[string]interface{}{"firewall_policy": p}, nil
+// ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map.
+func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "firewall_policy")
 }
 
 // Update allows firewall policies to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPolicyUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToFirewallPolicyUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular firewall policy based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
 
-func InsertRule(c *gophercloud.ServiceClient, policyID, ruleID, beforeID, afterID string) error {
-	type request struct {
-		RuleId string `json:"firewall_rule_id"`
-		Before string `json:"insert_before,omitempty"`
-		After  string `json:"insert_after,omitempty"`
-	}
-
-	reqBody := request{
-		RuleId: ruleID,
-		Before: beforeID,
-		After:  afterID,
-	}
-
-	// Send request to API
-	var res commonResult
-	_, res.Err = c.Put(insertURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res.Err
+type InsertRuleOptsBuilder interface {
+	ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error)
 }
 
-func RemoveRule(c *gophercloud.ServiceClient, policyID, ruleID string) error {
-	type request struct {
-		RuleId string `json:"firewall_rule_id"`
-	}
+type InsertRuleOpts struct {
+	ID           string `json:"firewall_rule_id" required:"true"`
+	BeforeRuleID string `json:"insert_before,omitempty"`
+	AfterRuleID  string `json:"insert_after,omitempty"`
+}
 
-	reqBody := request{
-		RuleId: ruleID,
-	}
+func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
+}
 
-	// Send request to API
-	var res commonResult
-	_, res.Err = c.Put(removeURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
+func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) InsertRuleResult {
+	var r InsertRuleResult
+	b, err := opts.ToFirewallPolicyInsertRuleMap()
+	if err != nil {
+		r.Err = err
+		return r
+	}
+	_, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res.Err
+	return r
+}
+
+func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) RemoveRuleResult {
+	var r RemoveRuleResult
+	b := map[string]interface{}{"firewall_rule_id": ruleID}
+	_, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return r
 }
diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go
index 7bdea5b..9c5b186 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/results.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/results.go
@@ -85,3 +85,13 @@
 type CreateResult struct {
 	commonResult
 }
+
+// InsertRuleResult represents the result of an InsertRule operation.
+type InsertRuleResult struct {
+	commonResult
+}
+
+// RemoveRuleResult represents the result of a RemoveRule operation.
+type RemoveRuleResult struct {
+	commonResult
+}
diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go
index a4fd498..c6b3b97 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go
@@ -88,90 +88,42 @@
 
 // CreateOpts contains all the values needed to create a new firewall rule.
 type CreateOpts struct {
-	// Mandatory for create
-	Protocol string
-	Action   string
-	// Optional
-	TenantID             string
-	Name                 string
-	Description          string
-	IPVersion            int
-	SourceIPAddress      string
-	DestinationIPAddress string
-	SourcePort           string
-	DestinationPort      string
-	Shared               *bool
-	Enabled              *bool
+	Protocol             string                `json:"protocol" required:"true"`
+	Action               string                `json:"action" required:"true"`
+	TenantID             string                `json:"tenant_id,omitempty"`
+	Name                 string                `json:"name,omitempty"`
+	Description          string                `json:"description,omitempty"`
+	IPVersion            gophercloud.IPVersion `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"`
 }
 
 // ToRuleCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	if opts.Protocol == "" {
-		return nil, errProtocolRequired
-	}
-
-	if opts.Action == "" {
-		return nil, errActionRequired
-	}
-
-	r := make(map[string]interface{})
-
-	r["protocol"] = opts.Protocol
-	r["action"] = opts.Action
-
-	if opts.TenantID != "" {
-		r["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		r["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		r["description"] = opts.Description
-	}
-	if opts.IPVersion != 0 {
-		r["ip_version"] = opts.IPVersion
-	}
-	if opts.SourceIPAddress != "" {
-		r["source_ip_address"] = opts.SourceIPAddress
-	}
-	if opts.DestinationIPAddress != "" {
-		r["destination_ip_address"] = opts.DestinationIPAddress
-	}
-	if opts.SourcePort != "" {
-		r["source_port"] = opts.SourcePort
-	}
-	if opts.DestinationPort != "" {
-		r["destination_port"] = opts.DestinationPort
-	}
-	if opts.Shared != nil {
-		r["shared"] = *opts.Shared
-	}
-	if opts.Enabled != nil {
-		r["enabled"] = *opts.Enabled
-	}
-
-	return map[string]interface{}{"firewall_rule": r}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall_rule")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall rule
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+	var r CreateResult
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular firewall rule based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -183,103 +135,42 @@
 }
 
 // UpdateOpts contains the values used when updating a firewall rule.
-// Optional
 type UpdateOpts struct {
-	Protocol             string
-	Action               string
-	Name                 string
-	Description          string
-	IPVersion            int
-	SourceIPAddress      *string
-	DestinationIPAddress *string
-	SourcePort           *string
-	DestinationPort      *string
-	Shared               *bool
-	Enabled              *bool
+	Protocol             string                `json:"protocol,omitempty"`
+	Action               string                `json:"action,omitempty"`
+	Name                 string                `json:"name,omitempty"`
+	Description          string                `json:"description,omitempty"`
+	IPVersion            gophercloud.IPVersion `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"`
 }
 
 // ToRuleUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) {
-	r := make(map[string]interface{})
-
-	if opts.Protocol != "" {
-		r["protocol"] = opts.Protocol
-	}
-	if opts.Action != "" {
-		r["action"] = opts.Action
-	}
-	if opts.Name != "" {
-		r["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		r["description"] = opts.Description
-	}
-	if opts.IPVersion != 0 {
-		r["ip_version"] = opts.IPVersion
-	}
-	if opts.SourceIPAddress != nil {
-		s := *opts.SourceIPAddress
-		if s == "" {
-			r["source_ip_address"] = nil
-		} else {
-			r["source_ip_address"] = s
-		}
-	}
-	if opts.DestinationIPAddress != nil {
-		s := *opts.DestinationIPAddress
-		if s == "" {
-			r["destination_ip_address"] = nil
-		} else {
-			r["destination_ip_address"] = s
-		}
-	}
-	if opts.SourcePort != nil {
-		s := *opts.SourcePort
-		if s == "" {
-			r["source_port"] = nil
-		} else {
-			r["source_port"] = s
-		}
-	}
-	if opts.DestinationPort != nil {
-		s := *opts.DestinationPort
-		if s == "" {
-			r["destination_port"] = nil
-		} else {
-			r["destination_port"] = s
-		}
-	}
-	if opts.Shared != nil {
-		r["shared"] = *opts.Shared
-	}
-	if opts.Enabled != nil {
-		r["enabled"] = *opts.Enabled
-	}
-
-	return map[string]interface{}{"firewall_rule": r}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall_rule")
 }
 
 // Update allows firewall policies to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToRuleUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToRuleUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular firewall rule based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
index 54477b8..ade033a 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
@@ -47,43 +47,17 @@
 // resource. The only required fields are FloatingNetworkID and PortID which
 // refer to the external network and internal port respectively.
 type CreateOpts struct {
-	FloatingNetworkID string
-	FloatingIP        string
-	PortID            string
-	FixedIP           string
-	TenantID          string
+	FloatingNetworkID string `json:"floating_network_id" required:"true"`
+	FloatingIP        string `json:"floating_ip_address,omitempty"`
+	PortID            string `json:"port_id,omitempty"`
+	FixedIP           string `json:"fixed_ip_address,omitempty"`
+	TenantID          string `json:"tenant_id,omitempty"`
 }
 
 // ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
 // interface
 func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
-	if opts.FloatingNetworkID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "floatingips.ToFloatingIPCreateMap"
-		err.Argument = "floatingips.CreateOpts.FloatingNetworkID"
-		return nil, err
-	}
-
-	i := map[string]string{
-		"floating_network_id": opts.FloatingNetworkID,
-	}
-
-	if opts.FloatingIP != "" {
-		i["floating_ip_address"] = opts.FloatingIP
-	}
-	if opts.PortID != "" {
-		i["port_id"] = opts.PortID
-	}
-	if opts.FixedIP != "" {
-		i["fixed_ip_address"] = opts.FixedIP
-	}
-	if opts.TenantID != "" {
-		i["tenant_id"] = opts.TenantID
-	}
-
-	b := make(map[string]interface{})
-	b["floatingip"] = i
-	return b, nil
+	return gophercloud.BuildRequestBody(opts, "floatingip")
 }
 
 // Create accepts a CreateOpts struct and uses the values provided to create a
@@ -112,22 +86,26 @@
 // returns a 409 error code.
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 	var r CreateResult
-
 	b, err := opts.ToFloatingIPCreateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
 	return r
 }
 
 // Get retrieves a particular floating IP resource based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
+}
+
+// UpdateOptsBuilder is the interface type must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+	ToFloatingIPUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a floating IP resource. The
@@ -135,45 +113,37 @@
 // linked to. To associate the floating IP with a new internal port, provide its
 // ID. To disassociate the floating IP from all ports, provide an empty string.
 type UpdateOpts struct {
-	PortID string
+	PortID string `json:"port_id,omitempty"`
+}
+
+// ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
+// interface
+func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "floatingip")
 }
 
 // Update allows floating IP resources to be updated. Currently, the only way to
 // "update" a floating IP is to associate it with a new internal port, or
 // disassociated it from all ports. See UpdateOpts for instructions of how to
 // do this.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type floatingIP struct {
-		PortID *string `json:"port_id"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var r UpdateResult
+	b, err := opts.ToFloatingIPUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	type request struct {
-		FloatingIP floatingIP `json:"floatingip"`
-	}
-
-	var portID *string
-	if opts.PortID == "" {
-		portID = nil
-	} else {
-		portID = &opts.PortID
-	}
-
-	reqBody := request{FloatingIP: floatingIP{PortID: portID}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular floating IP resource. Please
 // ensure this is what you want - you can also disassociate the IP from existing
 // internal ports.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go
index 910760f..d041d45 100644
--- a/openstack/networking/v2/extensions/layer3/routers/requests.go
+++ b/openstack/networking/v2/extensions/layer3/routers/requests.go
@@ -40,14 +40,22 @@
 	})
 }
 
+type CreateOptsBuilder interface {
+	ToRouterCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new router. There are
 // no required values.
 type CreateOpts struct {
-	Name         string
-	AdminStateUp *bool
-	Distributed  *bool
-	TenantID     string
-	GatewayInfo  *GatewayInfo
+	Name         string       `json:"name,omitempty"`
+	AdminStateUp *bool        `json:"admin_state_up,omitempty"`
+	Distributed  *bool        `json:"distributed,omitempty"`
+	TenantID     string       `json:"tenant_id,omitempty"`
+	GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
+}
+
+func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "router")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
@@ -58,49 +66,39 @@
 // GatewayInfo struct. The external gateway for the router must be plugged into
 // an external network (it is external if its `router:external' field is set to
 // true).
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type router struct {
-		Name         *string      `json:"name,omitempty"`
-		AdminStateUp *bool        `json:"admin_state_up,omitempty"`
-		Distributed  *bool        `json:"distributed,omitempty"`
-		TenantID     *string      `json:"tenant_id,omitempty"`
-		GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var r CreateResult
+	b, err := opts.ToRouterCreateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	type request struct {
-		Router router `json:"router"`
-	}
-
-	reqBody := request{Router: router{
-		Name:         gophercloud.MaybeString(opts.Name),
-		AdminStateUp: opts.AdminStateUp,
-		Distributed:  opts.Distributed,
-		TenantID:     gophercloud.MaybeString(opts.TenantID),
-	}}
-
-	if opts.GatewayInfo != nil {
-		reqBody.Router.GatewayInfo = opts.GatewayInfo
-	}
-
-	var res CreateResult
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular router based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
+}
+
+type UpdateOptsBuilder interface {
+	ToRouterUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a router.
 type UpdateOpts struct {
-	Name         string
-	AdminStateUp *bool
-	Distributed  *bool
-	GatewayInfo  *GatewayInfo
-	Routes       []Route
+	Name         string       `json:"name,omitempty"`
+	AdminStateUp *bool        `json:"admin_state_up,omitempty"`
+	Distributed  *bool        `json:"distributed,omitempty"`
+	GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
+	Routes       []Route      `json:"routes,omitempty"`
+}
+
+func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "router")
 }
 
 // Update allows routers to be updated. You can update the name, administrative
@@ -108,83 +106,43 @@
 // external gateway for a router, see Create. This operation does not enable
 // the update of router interfaces. To do this, use the AddInterface and
 // RemoveInterface functions.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type router struct {
-		Name         *string      `json:"name,omitempty"`
-		AdminStateUp *bool        `json:"admin_state_up,omitempty"`
-		Distributed  *bool        `json:"distributed,omitempty"`
-		GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
-		Routes       []Route      `json:"routes"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var r UpdateResult
+	b, err := opts.ToRouterUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	type request struct {
-		Router router `json:"router"`
-	}
-
-	reqBody := request{Router: router{
-		Name:         gophercloud.MaybeString(opts.Name),
-		AdminStateUp: opts.AdminStateUp,
-		Distributed:  opts.Distributed,
-	}}
-
-	if opts.GatewayInfo != nil {
-		reqBody.Router.GatewayInfo = opts.GatewayInfo
-	}
-
-	if opts.Routes != nil {
-		reqBody.Router.Routes = opts.Routes
-	}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular router based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
 
-// InterfaceOptsBuilder is what types must satisfy to be used as AddInterface
+// AddInterfaceOptsBuilder is what types must satisfy to be used as AddInterface
 // options.
-type InterfaceOptsBuilder interface {
-	ToInterfaceAddMap() (map[string]interface{}, error)
+type AddInterfaceOptsBuilder interface {
+	ToRouterAddInterfaceMap() (map[string]interface{}, error)
 }
 
-// InterfaceOpts allow you to work with operations that either add or remote
+// AddInterfaceOpts allow you to work with operations that either add
 // an internal interface from a router.
-type InterfaceOpts struct {
-	SubnetID string
-	PortID   string
+type AddInterfaceOpts struct {
+	SubnetID string `json:"subnet_id" xor:"PortID"`
+	PortID   string `json:"port_id" xor:"SubnetID"`
 }
 
-// ToInterfaceAddMap allows InterfaceOpts to satisfy the InterfaceOptsBuilder
+// ToRouterAddInterfaceMap allows InterfaceOpts to satisfy the InterfaceOptsBuilder
 // interface
-func (opts InterfaceOpts) ToInterfaceAddMap() (map[string]interface{}, error) {
-	if (opts.SubnetID == "" && opts.PortID == "") || (opts.SubnetID != "" && opts.PortID != "") {
-		err := gophercloud.ErrInvalidInput{}
-		err.Function = "routers.ToInterfaceAddMap"
-		err.Argument = "routers.InterfaceOpts.SubnetID/routers.InterfaceOpts.PortID"
-		err.Info = "When adding a router interface, you must provide one and only" +
-			" one of a subnet ID or a port ID"
-		return nil, err
-	}
-
-	b := make(map[string]interface{})
-	if opts.SubnetID != "" {
-		b["subnet_id"] = opts.SubnetID
-	}
-	if opts.PortID != "" {
-		b["port_id"] = opts.PortID
-	}
-
-	return b, nil
+func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // AddInterface attaches a subnet to an internal router interface. You must
@@ -208,30 +166,38 @@
 // identifier of a new port created by this operation. After the operation
 // completes, the device ID of the port is set to the router ID, and the
 // device owner attribute is set to `network:router_interface'.
-func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOptsBuilder) InterfaceResult {
+func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOptsBuilder) InterfaceResult {
 	var r InterfaceResult
-
-	if id == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "routers.AddInterface"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
-	b, err := opts.ToInterfaceAddMap()
+	b, err := opts.ToRouterAddInterfaceMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
 	return r
 }
 
+// RemoveInterfaceOptsBuilder is what types must satisfy to be used as RemoveInterface
+// options.
+type RemoveInterfaceOptsBuilder interface {
+	ToRouterRemoveInterfaceMap() (map[string]interface{}, error)
+}
+
+// RemoveInterfaceOpts allow you to work with operations that either add or remote
+// an internal interface from a router.
+type RemoveInterfaceOpts struct {
+	SubnetID string `json:"subnet_id" or:"PortID"`
+	PortID   string `json:"port_id" or:"SubnetID"`
+}
+
+// ToRouterRemoveInterfaceMap allows RemoveInterfaceOpts to satisfy the RemoveInterfaceOptsBuilder
+// interface
+func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
+}
+
 // RemoveInterface removes an internal router interface, which detaches a
 // subnet from the router. You must specify either a SubnetID or PortID, since
 // these values are used to identify the router interface to remove.
@@ -245,19 +211,15 @@
 // visible to you, the operation will fail and a 404 Not Found error will be
 // returned. After this operation completes, the port connecting the router
 // with the subnet is removed from the subnet for the network.
-func RemoveInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
-	var res InterfaceResult
-
-	type request struct {
-		SubnetID string `json:"subnet_id,omitempty"`
-		PortID   string `json:"port_id,omitempty"`
+func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfaceOptsBuilder) InterfaceResult {
+	var r InterfaceResult
+	b, err := opts.ToRouterRemoveInterfaceMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
-
-	_, res.Err = c.Put(removeInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return r
 }
diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go
index 48dd91c..9388a10 100644
--- a/openstack/networking/v2/extensions/lbaas/members/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/members/requests.go
@@ -42,82 +42,78 @@
 	})
 }
 
+type CreateOptsBuilder interface {
+	ToLBMemberCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new pool member.
 type CreateOpts struct {
+	// The IP address of the member.
+	Address string `json:"address" required:"true"`
+	// The port on which the application is hosted.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// The pool to which this member will belong.
+	PoolID string `json:"pool_id" required:"true"`
 	// Only required if the caller has an admin role and wants to create a pool
 	// for another tenant.
-	TenantID string
+	TenantID string `json:"tenant_id,omitempty" required:"true"`
+}
 
-	// Required. The IP address of the member.
-	Address string
-
-	// Required. The port on which the application is hosted.
-	ProtocolPort int
-
-	// Required. The pool to which this member will belong.
-	PoolID string
+func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
 // load balancer pool member.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type member struct {
-		TenantID     string `json:"tenant_id,omitempty"`
-		ProtocolPort int    `json:"protocol_port"`
-		Address      string `json:"address"`
-		PoolID       string `json:"pool_id"`
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var r CreateResult
+	b, err := opts.ToLBMemberCreateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-	type request struct {
-		Member member `json:"member"`
-	}
-
-	reqBody := request{Member: member{
-		Address:      opts.Address,
-		TenantID:     opts.TenantID,
-		ProtocolPort: opts.ProtocolPort,
-		PoolID:       opts.PoolID,
-	}}
-
-	var res CreateResult
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular pool member based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
+}
+
+type UpdateOptsBuilder interface {
+	ToLBMemberUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a pool member.
 type UpdateOpts struct {
 	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp bool
+	AdminStateUp bool `json:"admin_state_up,omitempty"`
+}
+
+func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
 // Update allows members to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type member struct {
-		AdminStateUp bool `json:"admin_state_up"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var r UpdateResult
+	b, err := opts.ToLBMemberUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-	type request struct {
-		Member member `json:"member"`
-	}
-
-	reqBody := request{Member: member{AdminStateUp: opts.AdminStateUp}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular member based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
index d1a7942..f24e483 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
@@ -42,18 +42,20 @@
 		return pagination.Pager{Err: err}
 	}
 	u := rootURL(c) + q.String()
-
 	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
 		return MonitorPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
+// MonitorType is the type for all the types of LB monitors
+type MonitorType string
+
 // Constants that represent approved monitoring types.
 const (
-	TypePING  = "PING"
-	TypeTCP   = "TCP"
-	TypeHTTP  = "HTTP"
-	TypeHTTPS = "HTTPS"
+	TypePING  MonitorType = "PING"
+	TypeTCP   MonitorType = "TCP"
+	TypeHTTP  MonitorType = "HTTP"
+	TypeHTTPS MonitorType = "HTTPS"
 )
 
 // CreateOptsBuilder is what types must satisfy to be used as Create
@@ -64,126 +66,54 @@
 
 // CreateOpts contains all the values needed to create a new health monitor.
 type CreateOpts struct {
-	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
 	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
 	// sent by the load balancer to verify the member state.
-	Type string
-
+	Type MonitorType `json:"type" required:"true"`
 	// Required. The time, in seconds, between sending probes to members.
-	Delay int
-
+	Delay int `json:"delay" required:"true"`
 	// Required. Maximum number of seconds for a monitor to wait for a ping reply
 	// before it times out. The value must be less than the delay value.
-	Timeout int
-
+	Timeout int `json:"timeout" required:"true"`
 	// Required. Number of permissible ping failures before changing the member's
 	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
-
+	MaxRetries int `json:"max_retries" required:"true"`
 	// Required for HTTP(S) types. URI path that will be accessed if monitor type
 	// is HTTP or HTTPS.
-	URLPath string
-
+	URLPath string `json:"url_path,omitempty"`
 	// Required for HTTP(S) types. The HTTP method used for requests by the
 	// monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
+	HTTPMethod string `json:"http_method,omitempty"`
 	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
 	// monitor. You can either specify a single status like "200", or a range
 	// like "200-202".
-	ExpectedCodes string
-
-	AdminStateUp *bool
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	// Required for admins. Indicates the owner of the VIP.
+	TenantID     string `json:"tenant_id,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
 }
 
 // ToLBMonitorCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
 // interface
 func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) {
-	allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
-	if opts.Type == "" || allowed[opts.Type] == false {
-		err := gophercloud.ErrInvalidInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
-		err.Argument = "monitors.CreateOpts.Type"
-		err.Info = "Supported values are PING, TCP, HTTP and HTTPS"
-		return nil, err
-	}
-	if opts.Delay == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
-		err.Argument = "monitors.CreateOpts.Delay"
-		return nil, err
-	}
-	if opts.Timeout == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
-		err.Argument = "monitors.CreateOpts.Timeout"
-		return nil, err
-	}
-	if opts.MaxRetries == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
-		err.Argument = "monitors.CreateOpts.MaxRetries"
-		return nil, err
-	}
-
 	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
 		if opts.URLPath == "" {
 			err := gophercloud.ErrMissingInput{}
-			err.Function = "monitors.ToLBMonitorCreateMap"
 			err.Argument = "monitors.CreateOpts.URLPath"
 			return nil, err
 		}
 		if opts.ExpectedCodes == "" {
 			err := gophercloud.ErrMissingInput{}
-			err.Function = "monitors.ToLBMonitorCreateMap"
 			err.Argument = "monitors.CreateOpts.ExpectedCodes"
 			return nil, err
 		}
 	}
 	if opts.Delay < opts.Timeout {
 		err := gophercloud.ErrInvalidInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
 		err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
 		err.Info = "Delay must be greater than or equal to timeout"
 		return nil, err
 	}
-
-	i := map[string]interface{}{
-		"type":    opts.Type,
-		"delay":   opts.Delay,
-		"timeout": opts.Timeout,
-	}
-
-	if opts.MaxRetries != 0 {
-		i["max_retries"] = opts.MaxRetries
-	}
-
-	if opts.TenantID != "" {
-		i["tenant_id"] = opts.TenantID
-	}
-
-	if opts.URLPath != "" {
-		i["url_path"] = opts.URLPath
-	}
-
-	if opts.ExpectedCodes != "" {
-		i["expected_codes"] = opts.ExpectedCodes
-	}
-
-	if opts.HTTPMethod != "" {
-		i["http_method"] = opts.HTTPMethod
-	}
-
-	if opts.AdminStateUp != nil {
-		i["admin_state_up"] = &opts.AdminStateUp
-	}
-
-	b := make(map[string]interface{})
-	b["health_monitor"] = i
-
-	return b, nil
+	return gophercloud.BuildRequestBody(opts, "health_monitor")
 }
 
 // Create is an operation which provisions a new health monitor. There are
@@ -202,22 +132,20 @@
 //
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 	var r CreateResult
-
 	b, err := opts.ToLBMonitorCreateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
 	return r
 }
 
 // Get retrieves a particular health monitor based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is what types must satisfy to be used as Update
@@ -230,31 +158,25 @@
 // Attributes not listed here but appear in CreateOpts are immutable and cannot
 // be updated.
 type UpdateOpts struct {
-	// Required. The time, in seconds, between sending probes to members.
-	Delay int
-
-	// Required. Maximum number of seconds for a monitor to wait for a ping reply
+	// The time, in seconds, between sending probes to members.
+	Delay int `json:"delay,omitempty"`
+	// Maximum number of seconds for a monitor to wait for a ping reply
 	// before it times out. The value must be less than the delay value.
-	Timeout int
-
-	// Required. Number of permissible ping failures before changing the member's
+	Timeout int `json:"timeout,omitempty"`
+	// Number of permissible ping failures before changing the member's
 	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
-
-	// Required for HTTP(S) types. URI path that will be accessed if monitor type
+	MaxRetries int `json:"max_retries,omitempty"`
+	// URI path that will be accessed if monitor type
 	// is HTTP or HTTPS.
-	URLPath string
-
-	// Required for HTTP(S) types. The HTTP method used for requests by the
+	URLPath string `json:"url_path,omitempty"`
+	// The HTTP method used for requests by the
 	// monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
-	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	HTTPMethod string `json:"http_method,omitempty"`
+	// Expected HTTP codes for a passing HTTP(S)
 	// monitor. You can either specify a single status like "200", or a range
 	// like "200-202".
-	ExpectedCodes string
-
-	AdminStateUp *bool
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	AdminStateUp  *bool  `json:"admin_state_up,omitempty"`
 }
 
 // ToLBMonitorUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
@@ -262,71 +184,31 @@
 func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) {
 	if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
 		err := gophercloud.ErrInvalidInput{}
-		err.Function = "monitors.ToLBMonitorCreateMap"
 		err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
 		err.Value = fmt.Sprintf("%d/%d", opts.Delay, opts.Timeout)
 		err.Info = "Delay must be greater than or equal to timeout"
 		return nil, err
 	}
-
-	i := map[string]interface{}{
-		"delay":       opts.Delay,
-		"timeout":     opts.Timeout,
-		"max_retries": opts.MaxRetries,
-	}
-
-	if opts.URLPath != "" {
-		i["url_path"] = opts.URLPath
-	}
-
-	if opts.ExpectedCodes != "" {
-		i["expected_codes"] = opts.ExpectedCodes
-	}
-
-	if opts.HTTPMethod != "" {
-		i["http_method"] = opts.HTTPMethod
-	}
-
-	if opts.AdminStateUp != nil {
-		i["admin_state_up"] = *opts.AdminStateUp
-	}
-
-	b := make(map[string]interface{})
-	b["health_monitor"] = i
-
-	fmt.Printf("b: %+v\n", b)
-
-	return b, nil
+	return gophercloud.BuildRequestBody(opts, "health_monitor")
 }
 
 // Update is an operation which modifies the attributes of the specified monitor.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 	var r UpdateResult
-
-	if id == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "monitors.Update"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
 	b, err := opts.ToLBMonitorUpdateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
 	return r
 }
 
 // Delete will permanently delete a particular monitor based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go
index bf3eef0..a79fd01 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go
@@ -43,112 +43,112 @@
 	})
 }
 
+// LBMethod is a type used for possible load balancing methods
+type LBMethod string
+
+// LBProtocol is a type used for possible load balancing protocols
+type LBProtocol string
+
 // Supported attributes for create/update operations.
 const (
-	LBMethodRoundRobin       = "ROUND_ROBIN"
-	LBMethodLeastConnections = "LEAST_CONNECTIONS"
+	LBMethodRoundRobin       LBMethod = "ROUND_ROBIN"
+	LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS"
 
-	ProtocolTCP   = "TCP"
-	ProtocolHTTP  = "HTTP"
-	ProtocolHTTPS = "HTTPS"
+	ProtocolTCP   LBProtocol = "TCP"
+	ProtocolHTTP  LBProtocol = "HTTP"
+	ProtocolHTTPS LBProtocol = "HTTPS"
 )
 
+// CreateOptsBuilder is the interface types must satisfy to be used as options
+// for the Create function
+type CreateOptsBuilder interface {
+	ToLBPoolCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new pool.
 type CreateOpts struct {
+	// Name of the pool.
+	Name string `json:"name" required:"true"`
+	// The protocol used by the pool members, you can use either
+	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
+	Protocol LBProtocol `json:"protocol" required:"true"`
 	// Only required if the caller has an admin role and wants to create a pool
 	// for another tenant.
-	TenantID string
-
-	// Required. Name of the pool.
-	Name string
-
-	// Required. The protocol used by the pool members, you can use either
-	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
-	Protocol string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// 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
-
+	SubnetID string `json:"subnet_id,omitempty"`
 	// The algorithm used to distribute load between the members of the pool. The
 	// current specification supports LBMethodRoundRobin and
 	// LBMethodLeastConnections as valid values for this attribute.
-	LBMethod string
+	LBMethod LBMethod `json:"lb_method" required:"true"`
 }
 
-// Create accepts a CreateOpts struct and uses the values to create a new
+// ToLBPoolCreateMap allows CreateOpts to satisfy the CreateOptsBuilder interface
+func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "pool")
+}
+
+// Create accepts a CreateOptsBuilder and uses the values to create a new
 // load balancer pool.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type pool struct {
-		Name     string `json:"name"`
-		TenantID string `json:"tenant_id,omitempty"`
-		Protocol string `json:"protocol"`
-		SubnetID string `json:"subnet_id"`
-		LBMethod string `json:"lb_method"`
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var r CreateResult
+	b, err := opts.ToLBPoolCreateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-	type request struct {
-		Pool pool `json:"pool"`
-	}
-
-	reqBody := request{Pool: pool{
-		Name:     opts.Name,
-		TenantID: opts.TenantID,
-		Protocol: opts.Protocol,
-		SubnetID: opts.SubnetID,
-		LBMethod: opts.LBMethod,
-	}}
-
-	var res CreateResult
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular pool based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
+}
+
+// UpdateOptsBuilder is the interface types must satisfy to be used as options
+// for the Update function
+type UpdateOptsBuilder interface {
+	ToLBPoolUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a pool.
 type UpdateOpts struct {
-	// Required. Name of the pool.
-	Name string
-
+	// Name of the pool.
+	Name string `json:"name,omitempty"`
 	// The algorithm used to distribute load between the members of the pool. The
 	// current specification supports LBMethodRoundRobin and
 	// LBMethodLeastConnections as valid values for this attribute.
-	LBMethod string
+	LBMethod LBMethod `json:"lb_method,omitempty"`
+}
+
+// ToLBPoolUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface
+func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "pool")
 }
 
 // Update allows pools to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type pool struct {
-		Name     string `json:"name,"`
-		LBMethod string `json:"lb_method"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var r UpdateResult
+	b, err := opts.ToLBPoolUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-	type request struct {
-		Pool pool `json:"pool"`
-	}
-
-	reqBody := request{Pool: pool{
-		Name:     opts.Name,
-		LBMethod: opts.LBMethod,
-	}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular pool based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
 
 // AssociateMonitor will associate a health monitor with a particular pool.
@@ -157,25 +157,17 @@
 // member can be deactivated (status set to INACTIVE) if any of health monitors
 // finds it unhealthy.
 func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
-	type hm struct {
-		ID string `json:"id"`
-	}
-	type request struct {
-		Monitor hm `json:"health_monitor"`
-	}
-
-	reqBody := request{hm{ID: monitorID}}
-
-	var res AssociateResult
-	_, res.Err = c.Post(associateURL(c, poolID), reqBody, &res.Body, nil)
-	return res
+	var r AssociateResult
+	b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}}
+	_, r.Err = c.Post(associateURL(c, poolID), b, &r.Body, nil)
+	return r
 }
 
 // DisassociateMonitor will disassociate a health monitor with a particular
 // pool. When dissociation is successful, the health monitor will no longer
 // check for the health of the members of the pool.
 func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
-	var res AssociateResult
-	_, res.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil)
-	return res
+	var r AssociateResult
+	_, r.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go
index 0f99fe8..52f167d 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go
@@ -5,19 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
 // ListOpts allows the filtering and sorting of paginated collections through
 // the API. Filtering is achieved by passing in struct field values that map to
 // the floating IP attributes you want to see returned. SortKey allows you to
@@ -66,107 +53,37 @@
 
 // CreateOpts contains all the values needed to create a new virtual IP.
 type CreateOpts struct {
-	// Required. Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
-	// Required. The network on which to allocate the VIP's address. A tenant can
+	// Human-readable name for the VIP. Does not have to be unique.
+	Name string `json:"name" required:"true"`
+	// The network on which to allocate the VIP's address. A tenant can
 	// only create VIPs on networks authorized by policy (e.g. networks that
 	// belong to them or networks that are shared).
-	SubnetID string
-
-	// Required. The protocol - can either be TCP, HTTP or HTTPS.
-	Protocol string
-
-	// Required. The port on which to listen for client traffic.
-	ProtocolPort int
-
-	// Required. The ID of the pool with which the VIP is associated.
-	PoolID string
-
+	SubnetID string `json:"subnet_id" required:"true"`
+	// The protocol - can either be TCP, HTTP or HTTPS.
+	Protocol string `json:"protocol" required:"true"`
+	// The port on which to listen for client traffic.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// The ID of the pool with which the VIP is associated.
+	PoolID string `json:"pool_id" required:"true"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
-	// Optional. The IP address of the VIP.
-	Address string
-
-	// Optional. Human-readable description for the VIP.
-	Description string
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The maximum number of connections allowed for the VIP.
-	ConnLimit *int
-
-	// Optional. The administrative state of the VIP. A valid value is true (UP)
+	TenantID string `json:"tenant_id,omitempty"`
+	// The IP address of the VIP.
+	Address string `json:"address,omitempty"`
+	// Human-readable description for the VIP.
+	Description string `json:"description,omitempty"`
+	// Omit this field to prevent session persistence.
+	Persistence *SessionPersistence `json:"session_persistence,omitempty"`
+	// The maximum number of connections allowed for the VIP.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// The administrative state of the VIP. A valid value is true (UP)
 	// or false (DOWN).
-	AdminStateUp *bool
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
 }
 
 // ToVIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
 // interface
 func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "vips.ToVIPCreateMap"
-		err.Argument = "vips.CreateOpts.Name"
-		return nil, err
-	}
-	if opts.SubnetID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "vips.ToVIPCreateMap"
-		err.Argument = "vips.CreateOpts.SubnetID"
-		return nil, err
-	}
-	if opts.Protocol == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "vips.ToVIPCreateMap"
-		err.Argument = "vips.CreateOpts.Protocol"
-		return nil, err
-	}
-	if opts.ProtocolPort == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "vips.ToVIPCreateMap"
-		err.Argument = "vips.CreateOpts.ProtocolPort"
-		return nil, err
-	}
-	if opts.PoolID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "vips.ToVIPCreateMap"
-		err.Argument = "vips.CreateOpts.PoolID"
-		return nil, err
-	}
-
-	i := map[string]interface{}{
-		"name":          opts.Name,
-		"subnet_id":     opts.SubnetID,
-		"protocol":      opts.Protocol,
-		"protocol_port": opts.ProtocolPort,
-		"pool_id":       opts.PoolID,
-	}
-	if opts.Description != "" {
-		i["description"] = opts.Description
-	}
-	if opts.TenantID != "" {
-		i["tenant_id"] = opts.TenantID
-	}
-	if opts.Address != "" {
-		i["address"] = opts.Address
-	}
-	if opts.Persistence != nil {
-		i["session_persistence"] = opts.Persistence
-	}
-	if opts.ConnLimit != nil {
-		i["connection_limit"] = opts.ConnLimit
-	}
-	if opts.AdminStateUp != nil {
-		i["admin_state_up"] = opts.AdminStateUp
-	}
-
-	b := make(map[string]interface{})
-	b["vip"] = i
-
-	return b, nil
+	return gophercloud.BuildRequestBody(opts, "vip")
 }
 
 // Create is an operation which provisions a new virtual IP based on the
@@ -182,22 +99,26 @@
 // specifying a TenantID attribute different than their own.
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 	var r CreateResult
-
 	b, err := opts.ToVIPCreateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
 	return r
 }
 
 // Get retrieves a particular virtual IP based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
+}
+
+// UpdateOptsBuilder is what types must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+	ToVIPUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains all the values needed to update an existing virtual IP.
@@ -205,63 +126,42 @@
 // be updated.
 type UpdateOpts struct {
 	// Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
-	// Required. The ID of the pool with which the VIP is associated.
-	PoolID string
-
-	// Optional. Human-readable description for the VIP.
-	Description string
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The maximum number of connections allowed for the VIP.
-	ConnLimit *int
-
-	// Optional. The administrative state of the VIP. A valid value is true (UP)
+	Name string `json:"name" required:"true"`
+	// The ID of the pool with which the VIP is associated.
+	PoolID string `json:"pool_id" required:"true"`
+	// Human-readable description for the VIP.
+	Description string `json:"description"`
+	// Omit this field to prevent session persistence.
+	Persistence *SessionPersistence `json:"session_persistence,omitempty"`
+	// The maximum number of connections allowed for the VIP.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// The administrative state of the VIP. A valid value is true (UP)
 	// or false (DOWN).
-	AdminStateUp *bool
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
+
+// ToVIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface
+func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "vip")
 }
 
 // Update is an operation which modifies the attributes of the specified VIP.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type vip struct {
-		Name         string              `json:"name,omitempty"`
-		PoolID       string              `json:"pool_id,omitempty"`
-		Description  *string             `json:"description,omitempty"`
-		Persistence  *SessionPersistence `json:"session_persistence,omitempty"`
-		ConnLimit    *int                `json:"connection_limit,omitempty"`
-		AdminStateUp *bool               `json:"admin_state_up,omitempty"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var r UpdateResult
+	b, err := opts.ToVIPUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	type request struct {
-		VirtualIP vip `json:"vip"`
-	}
-
-	reqBody := request{VirtualIP: vip{
-		Name:         opts.Name,
-		PoolID:       opts.PoolID,
-		Description:  gophercloud.MaybeString(opts.Description),
-		ConnLimit:    opts.ConnLimit,
-		AdminStateUp: opts.AdminStateUp,
-	}}
-
-	if opts.Persistence != nil {
-		reqBody.VirtualIP.Persistence = opts.Persistence
-	}
-
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return r
 }
 
 // Delete will permanently delete a particular virtual IP based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go
index 394045a..229013b 100755
--- a/openstack/networking/v2/extensions/provider/results.go
+++ b/openstack/networking/v2/extensions/provider/results.go
@@ -5,19 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
 // NetworkExtAttrs represents an extended form of a Network with additional fields.
 type NetworkExtAttrs struct {
 	// UUID for the network
diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go
index 9adce66..5211a14 100644
--- a/openstack/networking/v2/extensions/security/groups/requests.go
+++ b/openstack/networking/v2/extensions/security/groups/requests.go
@@ -34,77 +34,55 @@
 	})
 }
 
+type CreateOptsBuilder interface {
+	ToSecGroupCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new security group.
 type CreateOpts struct {
 	// Required. Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
+	Name string `json:"name" required:"true"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// Optional. Describes the security group.
-	Description string
+	Description string `json:"description,omitempty"`
+}
+
+func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Create is an operation which provisions a new security group with default
 // security group rules for the IPv4 and IPv6 ether types.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate required opts
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "groups.Create"
-		err.Argument = "groups.CreateOpts.Name"
-		res.Err = err
-		return res
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var r CreateResult
+	b, err := opts.ToSubnetCreateMap()
+	if err != nil {
+		r.Err = err
+		return r
 	}
-
-	type secgroup struct {
-		Name        string `json:"name"`
-		TenantID    string `json:"tenant_id,omitempty"`
-		Description string `json:"description,omitempty"`
-	}
-
-	type request struct {
-		SecGroup secgroup `json:"security_group"`
-	}
-
-	reqBody := request{SecGroup: secgroup{
-		Name:        opts.Name,
-		TenantID:    opts.TenantID,
-		Description: opts.Description,
-	}}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return r
 }
 
 // Get retrieves a particular security group based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // Delete will permanently delete a particular security group based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
 
 // IDFromName is a convenience function that returns a security group's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "groups.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	pages, err := List(client, ListOpts{}).AllPages()
 	if err != nil {
 		return "", err
@@ -124,19 +102,10 @@
 
 	switch count {
 	case 0:
-		err := &gophercloud.ErrResourceNotFound{}
-		err.Name = name
-		err.ResourceType = "group"
-		err.Function = "groups.IDFromName"
-		return "", err
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"}
 	case 1:
 		return id, nil
 	default:
-		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Count = count
-		err.Name = name
-		err.ResourceType = "group"
-		err.Function = "groups.IDFromName"
-		return "", err
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"}
 	}
 }
diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go
index 2da6227..8344718 100644
--- a/openstack/networking/v2/extensions/security/rules/requests.go
+++ b/openstack/networking/v2/extensions/security/rules/requests.go
@@ -41,12 +41,13 @@
 	})
 }
 
+type RuleDirection string
+type RuleProtocol string
+
 // Constants useful for CreateOpts
 const (
 	DirIngress   = "ingress"
 	DirEgress    = "egress"
-	Ether4       = "IPv4"
-	Ether6       = "IPv6"
 	ProtocolTCP  = "tcp"
 	ProtocolUDP  = "udp"
 	ProtocolICMP = "icmp"
@@ -62,106 +63,46 @@
 type CreateOpts struct {
 	// Required. Must be either "ingress" or "egress": the direction in which the
 	// security group rule is applied.
-	Direction string
-
+	Direction RuleDirection `json:"direction" required:"true"`
 	// Required. Must be "IPv4" or "IPv6", and addresses represented in CIDR must
 	// match the ingress or egress rules.
-	EtherType string
-
+	EtherType gophercloud.IPVersion `json:"ethertype" required:"true"`
 	// Required. The security group ID to associate with this security group rule.
-	SecGroupID string
-
+	SecGroupID string `json:"security_group_id" required:"true"`
 	// Optional. 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
-
+	PortRangeMax int `json:"port_range_max,omitempty"`
 	// Optional. 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
-
+	PortRangeMin int `json:"port_range_min,omitempty"`
 	// Optional. The protocol that is matched by the security group rule. Valid
 	// values are "tcp", "udp", "icmp" or an empty string.
-	Protocol string
-
+	Protocol RuleProtocol `json:"protocol,omitempty"`
 	// Optional. The remote group ID to be associated with this security group
 	// rule. You can specify either RemoteGroupID or RemoteIPPrefix.
-	RemoteGroupID string
-
+	RemoteGroupID string `json:"remote_group_id,omitempty"`
 	// Optional. 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
-
+	RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
+	TenantID string `json:"tenant_id,omitempty"`
 }
 
 // ToSecGroupRuleCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
 // interface
 func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) {
-	if opts.Direction != DirIngress && opts.Direction != DirEgress {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "rules.ToSecGroupRuleCreateMap"
-		err.Argument = "rules.CreateOpts.Direction"
-		return nil, err
-	}
-	if opts.EtherType != Ether4 && opts.EtherType != Ether6 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "rules.ToSecGroupRuleCreateMap"
-		err.Argument = "rules.CreateOpts.EtherType"
-		return nil, err
-	}
-	if opts.SecGroupID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "rules.ToSecGroupRuleCreateMap"
-		err.Argument = "rules.CreateOpts.SecGroupID"
-		return nil, err
-	}
-	if opts.Protocol != "" && opts.Protocol != ProtocolTCP && opts.Protocol != ProtocolUDP && opts.Protocol != ProtocolICMP {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "rules.ToSecGroupRuleCreateMap"
-		err.Argument = "rules.CreateOpts.Protocol"
-		return nil, err
-	}
-
-	i := map[string]interface{}{
-		"direction":         opts.Direction,
-		"ethertype":         opts.EtherType,
-		"security_group_id": opts.SecGroupID,
-	}
-	if opts.PortRangeMax != 0 {
-		i["port_range_max"] = opts.PortRangeMax
-	}
-	if opts.PortRangeMin != 0 {
-		i["port_range_min"] = opts.PortRangeMin
-	}
-	if opts.Protocol != "" {
-		i["protocol"] = opts.Protocol
-	}
-	if opts.RemoteGroupID != "" {
-		i["remote_group_id"] = opts.RemoteGroupID
-	}
-	if opts.RemoteIPPrefix != "" {
-		i["remote_ip_prefix"] = opts.RemoteIPPrefix
-	}
-	if opts.TenantID != "" {
-		i["tenant_id"] = opts.TenantID
-	}
-
-	b := make(map[string]interface{})
-	b["security_group_rule"] = i
-	return b, nil
+	return gophercloud.BuildRequestBody(opts, "security_group_rule")
 }
 
 // Create is an operation which adds a new security group rule and associates it
 // with an existing security group (whose ID is specified in CreateOpts).
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 	var r CreateResult
-
 	b, err := opts.ToSecGroupRuleCreateMap()
 	if err != nil {
 		r.Err = err
@@ -173,14 +114,14 @@
 
 // Get retrieves a particular security group rule based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return r
 }
 
 // Delete will permanently delete a particular security group rule based on its unique ID.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return r
 }
diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go
index 03fac14..ec043b1 100644
--- a/openstack/networking/v2/networks/requests.go
+++ b/openstack/networking/v2/networks/requests.go
@@ -5,26 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
-type networkOpts struct {
-	AdminStateUp *bool
-	Name         string
-	Shared       *bool
-	TenantID     string
-}
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -52,10 +32,7 @@
 // ToNetworkListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToNetworkListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -70,7 +47,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -78,9 +54,9 @@
 
 // Get retrieves a specific network based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return r
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -91,28 +67,17 @@
 	ToNetworkCreateMap() (map[string]interface{}, error)
 }
 
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts networkOpts
+// CreateOpts satisfies the CreateOptsBuilder interface
+type CreateOpts struct {
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
+	TenantID     string `json:"tenant_id,omitempty"`
+}
 
 // ToNetworkCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
-	n := make(map[string]interface{})
-
-	if opts.AdminStateUp != nil {
-		n["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		n["name"] = opts.Name
-	}
-	if opts.Shared != nil {
-		n["shared"] = &opts.Shared
-	}
-	if opts.TenantID != "" {
-		n["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"network": n}, nil
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // Create accepts a CreateOpts struct and creates a new network using the values
@@ -123,16 +88,14 @@
 // network. An admin user, however, has the option of specifying another tenant
 // ID in the CreateOpts struct.
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToNetworkCreateMap()
+	var r CreateResult
+	b, err := opts.ToNetworkCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -143,64 +106,44 @@
 	ToNetworkUpdateMap() (map[string]interface{}, error)
 }
 
-// UpdateOpts is the common options struct used in this package's Update
-// operation.
-type UpdateOpts networkOpts
+// UpdateOpts satisfies the UpdateOptsBuilder interface
+type UpdateOpts struct {
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
+}
 
 // ToNetworkUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
-	n := make(map[string]interface{})
-
-	if opts.AdminStateUp != nil {
-		n["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		n["name"] = opts.Name
-	}
-	if opts.Shared != nil {
-		n["shared"] = &opts.Shared
-	}
-
-	return map[string]interface{}{"network": n}, nil
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing network using the
 // values provided. For more information, see the Create function.
 func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToNetworkUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToNetworkUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(updateURL(c, networkID), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-
-	return res
+	return r
 }
 
 // Delete accepts a unique ID and deletes the network associated with it.
 func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, networkID), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(deleteURL(c, networkID), nil)
+	return r
 }
 
 // IDFromName is a convenience function that returns a network's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "networks.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	pages, err := List(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -220,19 +163,10 @@
 
 	switch count {
 	case 0:
-		err := &gophercloud.ErrResourceNotFound{}
-		err.Name = name
-		err.ResourceType = "network"
-		err.Function = "networks.IDFromName"
-		return "", err
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"}
 	case 1:
 		return id, nil
 	default:
-		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Count = count
-		err.Name = name
-		err.ResourceType = "network"
-		err.Function = "networks.IDFromName"
-		return "", err
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"}
 	}
 }
diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go
index d719f93..ab5d692 100644
--- a/openstack/networking/v2/ports/requests.go
+++ b/openstack/networking/v2/ports/requests.go
@@ -5,19 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -48,10 +35,7 @@
 // ToPortListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToPortListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -70,7 +54,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PortPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -78,9 +61,9 @@
 
 // Get retrieves a specific port based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return r
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -93,71 +76,34 @@
 
 // CreateOpts represents the attributes used when creating a new port.
 type CreateOpts struct {
-	NetworkID           string
-	Name                string
-	AdminStateUp        *bool
-	MACAddress          string
-	FixedIPs            interface{}
-	DeviceID            string
-	DeviceOwner         string
-	TenantID            string
-	SecurityGroups      []string
-	AllowedAddressPairs []AddressPair
+	NetworkID           string        `json:"network_id" required:"true"`
+	Name                string        `json:"name,omitempty"`
+	AdminStateUp        *bool         `json:"admin_state_up,omitempty"`
+	MACAddress          string        `json:"mac_address,omitempty"`
+	FixedIPs            interface{}   `json:"fixed_ips,omitempty"`
+	DeviceID            string        `json:"device_id,omitempty"`
+	DeviceOwner         string        `json:"device_owner,omitempty"`
+	TenantID            string        `json:"tenant_id,omitempty"`
+	SecurityGroups      []string      `json:"security_groups,omitempty"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"`
 }
 
 // ToPortCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.NetworkID == "" {
-		return nil, errNetworkIDRequired
-	}
-	p["network_id"] = opts.NetworkID
-
-	if opts.DeviceID != "" {
-		p["device_id"] = opts.DeviceID
-	}
-	if opts.DeviceOwner != "" {
-		p["device_owner"] = opts.DeviceOwner
-	}
-	if opts.FixedIPs != nil {
-		p["fixed_ips"] = opts.FixedIPs
-	}
-	if opts.SecurityGroups != nil {
-		p["security_groups"] = opts.SecurityGroups
-	}
-	if opts.TenantID != "" {
-		p["tenant_id"] = opts.TenantID
-	}
-	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.MACAddress != "" {
-		p["mac_address"] = opts.MACAddress
-	}
-	if opts.AllowedAddressPairs != nil {
-		p["allowed_address_pairs"] = opts.AllowedAddressPairs
-	}
-
-	return map[string]interface{}{"port": p}, nil
+	return gophercloud.BuildRequestBody(opts, "port")
 }
 
 // Create accepts a CreateOpts struct and creates a new network using the values
 // provided. You must remember to provide a NetworkID value.
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPortCreateMap()
+	var r CreateResult
+	b, err := opts.ToPortCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -170,79 +116,46 @@
 
 // UpdateOpts represents the attributes used when updating an existing port.
 type UpdateOpts struct {
-	Name                string
-	AdminStateUp        *bool
-	FixedIPs            interface{}
-	DeviceID            string
-	DeviceOwner         string
-	SecurityGroups      []string
-	AllowedAddressPairs []AddressPair
+	Name                string        `json:"name,omitempty"`
+	AdminStateUp        *bool         `json:"admin_state_up,omitempty"`
+	FixedIPs            interface{}   `json:"fixed_ips,omitempty"`
+	DeviceID            string        `json:"device_id,omitempty"`
+	DeviceOwner         string        `json:"device_owner,omitempty"`
+	SecurityGroups      []string      `json:"security_groups,omitempty"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"`
 }
 
 // ToPortUpdateMap casts an UpdateOpts struct to a map.
 func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.DeviceID != "" {
-		p["device_id"] = opts.DeviceID
-	}
-	if opts.DeviceOwner != "" {
-		p["device_owner"] = opts.DeviceOwner
-	}
-	if opts.FixedIPs != nil {
-		p["fixed_ips"] = opts.FixedIPs
-	}
-	if opts.SecurityGroups != nil {
-		p["security_groups"] = opts.SecurityGroups
-	}
-	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.AllowedAddressPairs != nil {
-		p["allowed_address_pairs"] = opts.AllowedAddressPairs
-	}
-
-	return map[string]interface{}{"port": p}, nil
+	return gophercloud.BuildRequestBody(opts, "port")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing port using the
 // values provided.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPortUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToPortUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return r
 }
 
 // Delete accepts a unique ID and deletes the port associated with it.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(deleteURL(c, id), nil)
+	return r
 }
 
 // IDFromName is a convenience function that returns a port's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "ports.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	pages, err := List(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -262,19 +175,10 @@
 
 	switch count {
 	case 0:
-		err := &gophercloud.ErrResourceNotFound{}
-		err.Name = name
-		err.ResourceType = "port"
-		err.Function = "ports.IDFromName"
-		return "", err
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "port"}
 	case 1:
 		return id, nil
 	default:
-		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Count = count
-		err.Name = name
-		err.ResourceType = "port"
-		err.Function = "ports.IDFromName"
-		return "", err
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "port"}
 	}
 }
diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go
index e55a034..a782cf5 100644
--- a/openstack/networking/v2/subnets/requests.go
+++ b/openstack/networking/v2/subnets/requests.go
@@ -5,19 +5,6 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -47,10 +34,7 @@
 // ToSubnetListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToSubnetListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -69,7 +53,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return SubnetPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -77,15 +60,19 @@
 
 // Get retrieves a specific subnet based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return r
 }
 
+// IPVersion is the IP address version for the subnet. Valid instances are
+// 4 and 6
+type IPVersion int
+
 // Valid IP types
 const (
-	IPv4 = 4
-	IPv6 = 6
+	IPv4 IPVersion = 4
+	IPv6 IPVersion = 6
 )
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -98,78 +85,34 @@
 
 // CreateOpts represents the attributes used when creating a new subnet.
 type CreateOpts struct {
-	// Required
-	NetworkID string
-	CIDR      string
-	// Optional
-	Name            string
-	TenantID        string
-	AllocationPools []AllocationPool
-	GatewayIP       string
-	IPVersion       int
-	EnableDHCP      *bool
-	DNSNameservers  []string
-	HostRoutes      []HostRoute
+	NetworkID       string           `json:"network_id" required:"true"`
+	CIDR            string           `json:"cidr" required:"true"`
+	Name            string           `json:"name,omitempty"`
+	TenantID        string           `json:"tenant_id,omitempty"`
+	AllocationPools []AllocationPool `json:"allocation_pools,omitempty"`
+	GatewayIP       string           `json:"gateway_ip,omitempty"`
+	IPVersion       IPVersion        `json:"ip_version,omitempty"`
+	EnableDHCP      *bool            `json:"enable_dhcp,omitempty"`
+	DNSNameservers  []string         `json:"dns_nameservers,omitempty"`
+	HostRoutes      []HostRoute      `json:"host_routes,omitempty"`
 }
 
 // ToSubnetCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.NetworkID == "" {
-		return nil, errNetworkIDRequired
-	}
-	if opts.CIDR == "" {
-		return nil, errCIDRRequired
-	}
-	if opts.IPVersion != 0 && opts.IPVersion != IPv4 && opts.IPVersion != IPv6 {
-		return nil, errInvalidIPType
-	}
-
-	s["network_id"] = opts.NetworkID
-	s["cidr"] = opts.CIDR
-
-	if opts.EnableDHCP != nil {
-		s["enable_dhcp"] = &opts.EnableDHCP
-	}
-	if opts.Name != "" {
-		s["name"] = opts.Name
-	}
-	if opts.GatewayIP != "" {
-		s["gateway_ip"] = opts.GatewayIP
-	}
-	if opts.TenantID != "" {
-		s["tenant_id"] = opts.TenantID
-	}
-	if opts.IPVersion != 0 {
-		s["ip_version"] = opts.IPVersion
-	}
-	if len(opts.AllocationPools) != 0 {
-		s["allocation_pools"] = opts.AllocationPools
-	}
-	if len(opts.DNSNameservers) != 0 {
-		s["dns_nameservers"] = opts.DNSNameservers
-	}
-	if len(opts.HostRoutes) != 0 {
-		s["host_routes"] = opts.HostRoutes
-	}
-
-	return map[string]interface{}{"subnet": s}, nil
+	return gophercloud.BuildRequestBody(opts, "subnet")
 }
 
 // Create accepts a CreateOpts struct and creates a new subnet using the values
 // provided. You must remember to provide a valid NetworkID, CIDR and IP version.
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToSubnetCreateMap()
+	var r CreateResult
+	b, err := opts.ToSubnetCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return r
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -180,72 +123,44 @@
 
 // UpdateOpts represents the attributes used when updating an existing subnet.
 type UpdateOpts struct {
-	Name           string
-	GatewayIP      string
-	DNSNameservers []string
-	HostRoutes     []HostRoute
-	EnableDHCP     *bool
+	Name           string      `json:"name,omitempty"`
+	GatewayIP      string      `json:"gateway_ip,omitempty"`
+	DNSNameservers []string    `json:"dns_nameservers,omitempty"`
+	HostRoutes     []HostRoute `json:"host_routes,omitempty"`
+	EnableDHCP     *bool       `json:"enable_dhcp,omitempty"`
 }
 
 // ToSubnetUpdateMap casts an UpdateOpts struct to a map.
 func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.EnableDHCP != nil {
-		s["enable_dhcp"] = &opts.EnableDHCP
-	}
-	if opts.Name != "" {
-		s["name"] = opts.Name
-	}
-	if opts.GatewayIP != "" {
-		s["gateway_ip"] = opts.GatewayIP
-	}
-	if opts.DNSNameservers != nil {
-		s["dns_nameservers"] = opts.DNSNameservers
-	}
-	if opts.HostRoutes != nil {
-		s["host_routes"] = opts.HostRoutes
-	}
-
-	return map[string]interface{}{"subnet": s}, nil
+	return gophercloud.BuildRequestBody(opts, "subnet")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing subnet using the
 // values provided.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToSubnetUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToSubnetUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-
-	return res
+	return r
 }
 
 // Delete accepts a unique ID and deletes the subnet associated with it.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(deleteURL(c, id), nil)
+	return r
 }
 
 // IDFromName is a convenience function that returns a subnet's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "subnets.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	pages, err := List(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -265,19 +180,10 @@
 
 	switch count {
 	case 0:
-		err := &gophercloud.ErrResourceNotFound{}
-		err.Name = name
-		err.ResourceType = "subnet"
-		err.Function = "subnets.IDFromName"
-		return "", err
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "subnet"}
 	case 1:
 		return id, nil
 	default:
-		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Count = count
-		err.Name = name
-		err.ResourceType = "subnet"
-		err.Function = "subnets.IDFromName"
-		return "", err
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "subnet"}
 	}
 }