Repository: ogen-go/ogen
Pull request #669 (2022-11-12 01:42:20)

fix(gen): check IP version when decoding


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 7826917b..c4544de3 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -132,5 +132,5 @@ issues:
     - linters: [ goconst ]
       text: 'string `(string|number|u?int\d{0,2})`'
     - linters: [ goconst ]
-      text: 'string `(Unix|unix|date|time)'
+      text: 'string `(Unix|unix|date|time|ipv6)'
 
diff --git a/gen/ir/faker.go b/gen/ir/faker.go
index 2b52f1e3..8c056152 100644
--- a/gen/ir/faker.go
+++ b/gen/ir/faker.go
@@ -32,6 +32,9 @@ func (t *Type) FakeValue() string {
 	case UUID:
 		return "uuid.New()"
 	case IP:
+		if s := t.Schema; s != nil && s.Format == "ipv6" {
+			return `netip.MustParseAddr("::1")`
+		}
 		return `netip.MustParseAddr("127.0.0.1")`
 	case URL:
 		return `url.URL{Scheme:"https", Host:"github.com", Path:"/ogen-go/ogen"}`
diff --git a/gen/ir/json.go b/gen/ir/json.go
index 8a5150b0..a2029cdf 100644
--- a/gen/ir/json.go
+++ b/gen/ir/json.go
@@ -137,8 +137,12 @@ func (j JSON) Format() string {
 		return "DateTime"
 	case "duration":
 		return "Duration"
-	case "ip", "ipv4", "ipv6":
+	case "ip":
 		return "IP"
+	case "ipv4":
+		return "IPv4"
+	case "ipv6":
+		return "IPv6"
 	case "uri":
 		return "URI"
 	case "int8",
diff --git a/internal/integration/sample_api/oas_defaults_gen.go b/internal/integration/sample_api/oas_defaults_gen.go
index 9ee85f98..165578ec 100644
--- a/internal/integration/sample_api/oas_defaults_gen.go
+++ b/internal/integration/sample_api/oas_defaults_gen.go
@@ -34,11 +34,11 @@ func (s *DefaultTest) setDefaults() {
 		s.IP.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"1.1.1.1\""))
+		val, _ := json.DecodeIPv4(jx.DecodeStr("\"1.1.1.1\""))
 		s.IPV4.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
+		val, _ := json.DecodeIPv6(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
 		s.IPV6.SetTo(val)
 	}
 	{
diff --git a/internal/integration/sample_api/oas_faker_gen.go b/internal/integration/sample_api/oas_faker_gen.go
index 37e31df9..0e8450d7 100644
--- a/internal/integration/sample_api/oas_faker_gen.go
+++ b/internal/integration/sample_api/oas_faker_gen.go
@@ -864,7 +864,7 @@ func (s *OptIPv4) SetFake() {
 func (s *OptIPv6) SetFake() {
 	var elem netip.Addr
 	{
-		elem = netip.MustParseAddr("127.0.0.1")
+		elem = netip.MustParseAddr("::1")
 	}
 	s.SetTo(elem)
 }
@@ -1139,7 +1139,7 @@ func (s *Pet) SetFake() {
 	}
 	{
 		{
-			s.IPV6 = netip.MustParseAddr("127.0.0.1")
+			s.IPV6 = netip.MustParseAddr("::1")
 		}
 	}
 	{
diff --git a/internal/integration/sample_api/oas_json_gen.go b/internal/integration/sample_api/oas_json_gen.go
index 1fab8573..2dc8337c 100644
--- a/internal/integration/sample_api/oas_json_gen.go
+++ b/internal/integration/sample_api/oas_json_gen.go
@@ -5125,7 +5125,7 @@ func (o OptIPv4) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5134,7 +5134,7 @@ func (o *OptIPv4) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv4 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -5160,7 +5160,7 @@ func (o OptIPv6) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5169,7 +5169,7 @@ func (o *OptIPv6) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv6 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -6163,12 +6163,12 @@ func (s Pet) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("ip_v4")
-		json.EncodeIP(e, s.IPV4)
+		json.EncodeIPv4(e, s.IPV4)
 	}
 	{
 
 		e.FieldStart("ip_v6")
-		json.EncodeIP(e, s.IPV6)
+		json.EncodeIPv6(e, s.IPV6)
 	}
 	{
 
@@ -6442,7 +6442,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v4":
 			requiredBitSet[1] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.IPV4 = v
 				if err != nil {
 					return err
@@ -6454,7 +6454,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v6":
 			requiredBitSet[1] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.IPV6 = v
 				if err != nil {
 					return err
diff --git a/internal/integration/sample_api_nc/oas_defaults_gen.go b/internal/integration/sample_api_nc/oas_defaults_gen.go
index 9ee85f98..165578ec 100644
--- a/internal/integration/sample_api_nc/oas_defaults_gen.go
+++ b/internal/integration/sample_api_nc/oas_defaults_gen.go
@@ -34,11 +34,11 @@ func (s *DefaultTest) setDefaults() {
 		s.IP.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"1.1.1.1\""))
+		val, _ := json.DecodeIPv4(jx.DecodeStr("\"1.1.1.1\""))
 		s.IPV4.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
+		val, _ := json.DecodeIPv6(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
 		s.IPV6.SetTo(val)
 	}
 	{
diff --git a/internal/integration/sample_api_nc/oas_json_gen.go b/internal/integration/sample_api_nc/oas_json_gen.go
index 1fab8573..2dc8337c 100644
--- a/internal/integration/sample_api_nc/oas_json_gen.go
+++ b/internal/integration/sample_api_nc/oas_json_gen.go
@@ -5125,7 +5125,7 @@ func (o OptIPv4) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5134,7 +5134,7 @@ func (o *OptIPv4) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv4 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -5160,7 +5160,7 @@ func (o OptIPv6) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5169,7 +5169,7 @@ func (o *OptIPv6) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv6 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -6163,12 +6163,12 @@ func (s Pet) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("ip_v4")
-		json.EncodeIP(e, s.IPV4)
+		json.EncodeIPv4(e, s.IPV4)
 	}
 	{
 
 		e.FieldStart("ip_v6")
-		json.EncodeIP(e, s.IPV6)
+		json.EncodeIPv6(e, s.IPV6)
 	}
 	{
 
@@ -6442,7 +6442,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v4":
 			requiredBitSet[1] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.IPV4 = v
 				if err != nil {
 					return err
@@ -6454,7 +6454,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v6":
 			requiredBitSet[1] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.IPV6 = v
 				if err != nil {
 					return err
diff --git a/internal/integration/sample_api_ns/oas_defaults_gen.go b/internal/integration/sample_api_ns/oas_defaults_gen.go
index 9ee85f98..165578ec 100644
--- a/internal/integration/sample_api_ns/oas_defaults_gen.go
+++ b/internal/integration/sample_api_ns/oas_defaults_gen.go
@@ -34,11 +34,11 @@ func (s *DefaultTest) setDefaults() {
 		s.IP.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"1.1.1.1\""))
+		val, _ := json.DecodeIPv4(jx.DecodeStr("\"1.1.1.1\""))
 		s.IPV4.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
+		val, _ := json.DecodeIPv6(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
 		s.IPV6.SetTo(val)
 	}
 	{
diff --git a/internal/integration/sample_api_ns/oas_json_gen.go b/internal/integration/sample_api_ns/oas_json_gen.go
index 1fab8573..2dc8337c 100644
--- a/internal/integration/sample_api_ns/oas_json_gen.go
+++ b/internal/integration/sample_api_ns/oas_json_gen.go
@@ -5125,7 +5125,7 @@ func (o OptIPv4) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5134,7 +5134,7 @@ func (o *OptIPv4) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv4 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -5160,7 +5160,7 @@ func (o OptIPv6) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5169,7 +5169,7 @@ func (o *OptIPv6) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv6 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -6163,12 +6163,12 @@ func (s Pet) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("ip_v4")
-		json.EncodeIP(e, s.IPV4)
+		json.EncodeIPv4(e, s.IPV4)
 	}
 	{
 
 		e.FieldStart("ip_v6")
-		json.EncodeIP(e, s.IPV6)
+		json.EncodeIPv6(e, s.IPV6)
 	}
 	{
 
@@ -6442,7 +6442,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v4":
 			requiredBitSet[1] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.IPV4 = v
 				if err != nil {
 					return err
@@ -6454,7 +6454,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v6":
 			requiredBitSet[1] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.IPV6 = v
 				if err != nil {
 					return err
diff --git a/internal/integration/sample_api_nsnc/oas_defaults_gen.go b/internal/integration/sample_api_nsnc/oas_defaults_gen.go
index 9ee85f98..165578ec 100644
--- a/internal/integration/sample_api_nsnc/oas_defaults_gen.go
+++ b/internal/integration/sample_api_nsnc/oas_defaults_gen.go
@@ -34,11 +34,11 @@ func (s *DefaultTest) setDefaults() {
 		s.IP.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"1.1.1.1\""))
+		val, _ := json.DecodeIPv4(jx.DecodeStr("\"1.1.1.1\""))
 		s.IPV4.SetTo(val)
 	}
 	{
-		val, _ := json.DecodeIP(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
+		val, _ := json.DecodeIPv6(jx.DecodeStr("\"2001:db8:85a3::8a2e:370:7334\""))
 		s.IPV6.SetTo(val)
 	}
 	{
diff --git a/internal/integration/sample_api_nsnc/oas_json_gen.go b/internal/integration/sample_api_nsnc/oas_json_gen.go
index 1fab8573..2dc8337c 100644
--- a/internal/integration/sample_api_nsnc/oas_json_gen.go
+++ b/internal/integration/sample_api_nsnc/oas_json_gen.go
@@ -5125,7 +5125,7 @@ func (o OptIPv4) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5134,7 +5134,7 @@ func (o *OptIPv4) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv4 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -5160,7 +5160,7 @@ func (o OptIPv6) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -5169,7 +5169,7 @@ func (o *OptIPv6) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv6 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -6163,12 +6163,12 @@ func (s Pet) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("ip_v4")
-		json.EncodeIP(e, s.IPV4)
+		json.EncodeIPv4(e, s.IPV4)
 	}
 	{
 
 		e.FieldStart("ip_v6")
-		json.EncodeIP(e, s.IPV6)
+		json.EncodeIPv6(e, s.IPV6)
 	}
 	{
 
@@ -6442,7 +6442,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v4":
 			requiredBitSet[1] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.IPV4 = v
 				if err != nil {
 					return err
@@ -6454,7 +6454,7 @@ func (s *Pet) Decode(d *jx.Decoder) error {
 		case "ip_v6":
 			requiredBitSet[1] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.IPV6 = v
 				if err != nil {
 					return err
diff --git a/json/ip.go b/json/ip.go
index 97319eae..aa1ccd44 100644
--- a/json/ip.go
+++ b/json/ip.go
@@ -7,22 +7,49 @@ import (
 	"github.com/go-faster/jx"
 )
 
-// DecodeIP decodes netip.Addr.
-func DecodeIP(i *jx.Decoder) (v netip.Addr, err error) {
-	s, err := i.Str()
+func decodeIP(d *jx.Decoder, checkVersion func(addr netip.Addr) bool) (v netip.Addr, err error) {
+	raw, err := d.Str()
 	if err != nil {
 		return v, err
 	}
-	v, err = netip.ParseAddr(s)
+	v, err = netip.ParseAddr(raw)
 	if err != nil {
 		return v, errors.Wrap(err, "bad ip format")
 	}
+	if checkVersion != nil && !checkVersion(v) {
+		return v, errors.New("wrong ip version")
+	}
 	return v, nil
 }
 
+// DecodeIP decodes netip.Addr.
+func DecodeIP(d *jx.Decoder) (netip.Addr, error) {
+	return decodeIP(d, nil)
+}
+
+// DecodeIPv4 decodes netip.Addr.
+func DecodeIPv4(d *jx.Decoder) (netip.Addr, error) {
+	return decodeIP(d, netip.Addr.Is4)
+}
+
+// DecodeIPv6 decodes netip.Addr.
+func DecodeIPv6(d *jx.Decoder) (netip.Addr, error) {
+	return decodeIP(d, netip.Addr.Is6)
+}
+
 // EncodeIP encodes netip.Addr.
 func EncodeIP(s *jx.Encoder, v netip.Addr) {
 	b := make([]byte, 64)
 	b = v.AppendTo(b[:0])
 	s.ByteStr(b)
 }
+
+// EncodeIPv4 encodes netip.Addr.
+func EncodeIPv4(s *jx.Encoder, v netip.Addr) {
+	EncodeIP(s, v)
+}
+
+// EncodeIPv6 encodes netip.Addr.
+func EncodeIPv6(s *jx.Encoder, v netip.Addr) {
+	EncodeIP(s, v)
+}


Test patch:
diff --git a/examples/ex_test_format/oas_faker_gen.go b/examples/ex_test_format/oas_faker_gen.go
index 71326e1a..469e7eae 100644
--- a/examples/ex_test_format/oas_faker_gen.go
+++ b/examples/ex_test_format/oas_faker_gen.go
@@ -246,7 +246,7 @@ func (s *OptIPv4) SetFake() {
 func (s *OptIPv6) SetFake() {
 	var elem netip.Addr
 	{
-		elem = netip.MustParseAddr("127.0.0.1")
+		elem = netip.MustParseAddr("::1")
 	}
 	s.SetTo(elem)
 }
@@ -1018,7 +1018,7 @@ func (s *TestRequestFormatTestReq) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.RequiredArrayStringIpv6 = append(s.RequiredArrayStringIpv6, elem)
 			}
@@ -1736,7 +1736,7 @@ func (s *TestRequestFormatTestReq) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
@@ -2063,7 +2063,7 @@ func (s *TestRequestFormatTestReq) SetFake() {
 	}
 	{
 		{
-			s.RequiredStringIpv6 = netip.MustParseAddr("127.0.0.1")
+			s.RequiredStringIpv6 = netip.MustParseAddr("::1")
 		}
 	}
 	{
@@ -2494,7 +2494,7 @@ func (s *TestRequestFormatTestReq) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.OptionalArrayStringIpv6 = append(s.OptionalArrayStringIpv6, elem)
 			}
@@ -3212,7 +3212,7 @@ func (s *TestRequestFormatTestReq) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
@@ -3978,7 +3978,7 @@ func (s *TestRequestRequiredFormatTestReq) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.RequiredArrayStringIpv6 = append(s.RequiredArrayStringIpv6, elem)
 			}
@@ -4696,7 +4696,7 @@ func (s *TestRequestRequiredFormatTestReq) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
@@ -5023,7 +5023,7 @@ func (s *TestRequestRequiredFormatTestReq) SetFake() {
 	}
 	{
 		{
-			s.RequiredStringIpv6 = netip.MustParseAddr("127.0.0.1")
+			s.RequiredStringIpv6 = netip.MustParseAddr("::1")
 		}
 	}
 	{
@@ -5454,7 +5454,7 @@ func (s *TestRequestRequiredFormatTestReq) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.OptionalArrayStringIpv6 = append(s.OptionalArrayStringIpv6, elem)
 			}
@@ -6172,7 +6172,7 @@ func (s *TestRequestRequiredFormatTestReq) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
@@ -6938,7 +6938,7 @@ func (s *TestResponseFormatTestOK) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.RequiredArrayStringIpv6 = append(s.RequiredArrayStringIpv6, elem)
 			}
@@ -7656,7 +7656,7 @@ func (s *TestResponseFormatTestOK) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
@@ -7983,7 +7983,7 @@ func (s *TestResponseFormatTestOK) SetFake() {
 	}
 	{
 		{
-			s.RequiredStringIpv6 = netip.MustParseAddr("127.0.0.1")
+			s.RequiredStringIpv6 = netip.MustParseAddr("::1")
 		}
 	}
 	{
@@ -8414,7 +8414,7 @@ func (s *TestResponseFormatTestOK) SetFake() {
 			for i := 0; i < 0; i++ {
 				var elem netip.Addr
 				{
-					elem = netip.MustParseAddr("127.0.0.1")
+					elem = netip.MustParseAddr("::1")
 				}
 				s.OptionalArrayStringIpv6 = append(s.OptionalArrayStringIpv6, elem)
 			}
@@ -9132,7 +9132,7 @@ func (s *TestResponseFormatTestOK) SetFake() {
 					for i := 0; i < 0; i++ {
 						var elemElem netip.Addr
 						{
-							elemElem = netip.MustParseAddr("127.0.0.1")
+							elemElem = netip.MustParseAddr("::1")
 						}
 						elem = append(elem, elemElem)
 					}
diff --git a/examples/ex_test_format/oas_json_gen.go b/examples/ex_test_format/oas_json_gen.go
index ec084a94..415aa00b 100644
--- a/examples/ex_test_format/oas_json_gen.go
+++ b/examples/ex_test_format/oas_json_gen.go
@@ -425,7 +425,7 @@ func (o NilIPv4) Encode(e *jx.Encoder) {
 		e.Null()
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -444,7 +444,7 @@ func (o *NilIPv4) Decode(d *jx.Decoder) error {
 		return nil
 	}
 	o.Null = false
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -471,7 +471,7 @@ func (o NilIPv6) Encode(e *jx.Encoder) {
 		e.Null()
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -490,7 +490,7 @@ func (o *NilIPv6) Decode(d *jx.Decoder) error {
 		return nil
 	}
 	o.Null = false
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -1681,7 +1681,7 @@ func (o OptIPv4) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -1690,7 +1690,7 @@ func (o *OptIPv4) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv4 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -1716,7 +1716,7 @@ func (o OptIPv6) Encode(e *jx.Encoder) {
 	if !o.Set {
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -1725,7 +1725,7 @@ func (o *OptIPv6) Decode(d *jx.Decoder) error {
 		return errors.New("invalid: unable to decode OptIPv6 to nil")
 	}
 	o.Set = true
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -2268,7 +2268,7 @@ func (o OptNilIPv4) Encode(e *jx.Encoder) {
 		e.Null()
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv4(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -2289,7 +2289,7 @@ func (o *OptNilIPv4) Decode(d *jx.Decoder) error {
 	}
 	o.Set = true
 	o.Null = false
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv4(d)
 	if err != nil {
 		return err
 	}
@@ -2319,7 +2319,7 @@ func (o OptNilIPv6) Encode(e *jx.Encoder) {
 		e.Null()
 		return
 	}
-	json.EncodeIP(e, o.Value)
+	json.EncodeIPv6(e, o.Value)
 }
 
 // Decode decodes netip.Addr from json.
@@ -2340,7 +2340,7 @@ func (o *OptNilIPv6) Decode(d *jx.Decoder) error {
 	}
 	o.Set = true
 	o.Null = false
-	v, err := json.DecodeIP(d)
+	v, err := json.DecodeIPv6(d)
 	if err != nil {
 		return err
 	}
@@ -4381,7 +4381,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv4")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv4 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv4(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -4390,7 +4390,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv6")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv6 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv6(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -4880,7 +4880,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv4 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -4893,7 +4893,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv6 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -5160,12 +5160,12 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("required_string_ipv4")
-		json.EncodeIP(e, s.RequiredStringIpv4)
+		json.EncodeIPv4(e, s.RequiredStringIpv4)
 	}
 	{
 
 		e.FieldStart("required_string_ipv6")
-		json.EncodeIP(e, s.RequiredStringIpv6)
+		json.EncodeIPv6(e, s.RequiredStringIpv6)
 	}
 	{
 
@@ -5527,7 +5527,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv4")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv4 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -5537,7 +5537,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv6")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv6 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -6068,7 +6068,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv4 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv4(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -6082,7 +6082,7 @@ func (s TestRequestFormatTestReq) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv6 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv6(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -7324,7 +7324,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -7344,7 +7344,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -8397,7 +8397,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -8425,7 +8425,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -9033,7 +9033,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 		case "required_string_ipv4":
 			requiredBitSet[14] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.RequiredStringIpv4 = v
 				if err != nil {
 					return err
@@ -9045,7 +9045,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 		case "required_string_ipv6":
 			requiredBitSet[14] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.RequiredStringIpv6 = v
 				if err != nil {
 					return err
@@ -9746,7 +9746,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -9765,7 +9765,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -10776,7 +10776,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -10803,7 +10803,7 @@ func (s *TestRequestFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -11881,7 +11881,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv4")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv4 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv4(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -11890,7 +11890,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv6")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv6 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv6(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -12380,7 +12380,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv4 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -12393,7 +12393,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv6 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -12660,12 +12660,12 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("required_string_ipv4")
-		json.EncodeIP(e, s.RequiredStringIpv4)
+		json.EncodeIPv4(e, s.RequiredStringIpv4)
 	}
 	{
 
 		e.FieldStart("required_string_ipv6")
-		json.EncodeIP(e, s.RequiredStringIpv6)
+		json.EncodeIPv6(e, s.RequiredStringIpv6)
 	}
 	{
 
@@ -13027,7 +13027,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv4")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv4 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -13037,7 +13037,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv6")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv6 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -13568,7 +13568,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv4 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv4(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -13582,7 +13582,7 @@ func (s TestRequestRequiredFormatTestReq) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv6 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv6(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -14824,7 +14824,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -14844,7 +14844,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -15897,7 +15897,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -15925,7 +15925,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -16533,7 +16533,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 		case "required_string_ipv4":
 			requiredBitSet[14] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.RequiredStringIpv4 = v
 				if err != nil {
 					return err
@@ -16545,7 +16545,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 		case "required_string_ipv6":
 			requiredBitSet[14] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.RequiredStringIpv6 = v
 				if err != nil {
 					return err
@@ -17246,7 +17246,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -17265,7 +17265,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -18276,7 +18276,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -18303,7 +18303,7 @@ func (s *TestRequestRequiredFormatTestReq) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -19381,7 +19381,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv4")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv4 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv4(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -19390,7 +19390,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 		e.FieldStart("required_array_string_ipv6")
 		e.ArrStart()
 		for _, elem := range s.RequiredArrayStringIpv6 {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv6(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -19880,7 +19880,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv4 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -19893,7 +19893,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 		for _, elem := range s.RequiredDoubleArrayStringIpv6 {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -20160,12 +20160,12 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 	{
 
 		e.FieldStart("required_string_ipv4")
-		json.EncodeIP(e, s.RequiredStringIpv4)
+		json.EncodeIPv4(e, s.RequiredStringIpv4)
 	}
 	{
 
 		e.FieldStart("required_string_ipv6")
-		json.EncodeIP(e, s.RequiredStringIpv6)
+		json.EncodeIPv6(e, s.RequiredStringIpv6)
 	}
 	{
 
@@ -20527,7 +20527,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv4")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv4 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -20537,7 +20537,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 			e.FieldStart("optional_array_string_ipv6")
 			e.ArrStart()
 			for _, elem := range s.OptionalArrayStringIpv6 {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -21068,7 +21068,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv4 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv4(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -21082,7 +21082,7 @@ func (s TestResponseFormatTestOK) encodeFields(e *jx.Encoder) {
 			for _, elem := range s.OptionalDoubleArrayStringIpv6 {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv6(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -22324,7 +22324,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -22344,7 +22344,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 				s.RequiredArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -23397,7 +23397,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -23425,7 +23425,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -24033,7 +24033,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 		case "required_string_ipv4":
 			requiredBitSet[14] |= 1 << 0
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				s.RequiredStringIpv4 = v
 				if err != nil {
 					return err
@@ -24045,7 +24045,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 		case "required_string_ipv6":
 			requiredBitSet[14] |= 1 << 1
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				s.RequiredStringIpv6 = v
 				if err != nil {
 					return err
@@ -24746,7 +24746,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv4 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -24765,7 +24765,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 				s.OptionalArrayStringIpv6 = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -25776,7 +25776,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -25803,7 +25803,7 @@ func (s *TestResponseFormatTestOK) Decode(d *jx.Decoder) error {
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
diff --git a/examples/ex_test_format/oas_request_decoders_gen.go b/examples/ex_test_format/oas_request_decoders_gen.go
index c419c95f..b085b45c 100644
--- a/examples/ex_test_format/oas_request_decoders_gen.go
+++ b/examples/ex_test_format/oas_request_decoders_gen.go
@@ -23039,7 +23039,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv4Request(r *http.Request) (
 
 		d := jx.DecodeBytes(buf)
 		if err := func() error {
-			v, err := json.DecodeIP(d)
+			v, err := json.DecodeIPv4(d)
 			request = v
 			if err != nil {
 				return err
@@ -23102,7 +23102,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv4ArrayRequest(r *http.Request
 			request = make([]netip.Addr, 0)
 			if err := d.Arr(func(d *jx.Decoder) error {
 				var elem netip.Addr
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				elem = v
 				if err != nil {
 					return err
@@ -23181,7 +23181,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv4ArrayArrayRequest(r *http.Re
 				elem = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elemElem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elemElem = v
 					if err != nil {
 						return err
@@ -23507,7 +23507,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv6Request(r *http.Request) (
 
 		d := jx.DecodeBytes(buf)
 		if err := func() error {
-			v, err := json.DecodeIP(d)
+			v, err := json.DecodeIPv6(d)
 			request = v
 			if err != nil {
 				return err
@@ -23570,7 +23570,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv6ArrayRequest(r *http.Request
 			request = make([]netip.Addr, 0)
 			if err := d.Arr(func(d *jx.Decoder) error {
 				var elem netip.Addr
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				elem = v
 				if err != nil {
 					return err
@@ -23649,7 +23649,7 @@ func (s *Server) decodeTestRequestRequiredStringIpv6ArrayArrayRequest(r *http.Re
 				elem = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elemElem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elemElem = v
 					if err != nil {
 						return err
@@ -33677,7 +33677,7 @@ func (s *Server) decodeTestRequestStringIpv4ArrayRequest(r *http.Request) (
 			request = make([]netip.Addr, 0)
 			if err := d.Arr(func(d *jx.Decoder) error {
 				var elem netip.Addr
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				elem = v
 				if err != nil {
 					return err
@@ -33751,7 +33751,7 @@ func (s *Server) decodeTestRequestStringIpv4ArrayArrayRequest(r *http.Request) (
 				elem = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elemElem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elemElem = v
 					if err != nil {
 						return err
@@ -34141,7 +34141,7 @@ func (s *Server) decodeTestRequestStringIpv6ArrayRequest(r *http.Request) (
 			request = make([]netip.Addr, 0)
 			if err := d.Arr(func(d *jx.Decoder) error {
 				var elem netip.Addr
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				elem = v
 				if err != nil {
 					return err
@@ -34215,7 +34215,7 @@ func (s *Server) decodeTestRequestStringIpv6ArrayArrayRequest(r *http.Request) (
 				elem = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elemElem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elemElem = v
 					if err != nil {
 						return err
diff --git a/examples/ex_test_format/oas_request_encoders_gen.go b/examples/ex_test_format/oas_request_encoders_gen.go
index 900bdda5..5a5f3ca0 100644
--- a/examples/ex_test_format/oas_request_encoders_gen.go
+++ b/examples/ex_test_format/oas_request_encoders_gen.go
@@ -5525,7 +5525,7 @@ func encodeTestRequestRequiredStringIpv4Request(
 	const contentType = "application/json"
 	e := jx.GetEncoder()
 	{
-		json.EncodeIP(e, req)
+		json.EncodeIPv4(e, req)
 	}
 	encoded := e.Bytes()
 	ht.SetBody(r, bytes.NewReader(encoded), contentType)
@@ -5541,7 +5541,7 @@ func encodeTestRequestRequiredStringIpv4ArrayRequest(
 	{
 		e.ArrStart()
 		for _, elem := range req {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv4(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -5561,7 +5561,7 @@ func encodeTestRequestRequiredStringIpv4ArrayArrayRequest(
 		for _, elem := range req {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -5633,7 +5633,7 @@ func encodeTestRequestRequiredStringIpv6Request(
 	const contentType = "application/json"
 	e := jx.GetEncoder()
 	{
-		json.EncodeIP(e, req)
+		json.EncodeIPv6(e, req)
 	}
 	encoded := e.Bytes()
 	ht.SetBody(r, bytes.NewReader(encoded), contentType)
@@ -5649,7 +5649,7 @@ func encodeTestRequestRequiredStringIpv6ArrayRequest(
 	{
 		e.ArrStart()
 		for _, elem := range req {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv6(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -5669,7 +5669,7 @@ func encodeTestRequestRequiredStringIpv6ArrayArrayRequest(
 		for _, elem := range req {
 			e.ArrStart()
 			for _, elem := range elem {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -8128,7 +8128,7 @@ func encodeTestRequestStringIpv4ArrayRequest(
 		if req != nil {
 			e.ArrStart()
 			for _, elem := range req {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv4(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -8150,7 +8150,7 @@ func encodeTestRequestStringIpv4ArrayArrayRequest(
 			for _, elem := range req {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv4(e, elem)
 				}
 				e.ArrEnd()
 			}
@@ -8256,7 +8256,7 @@ func encodeTestRequestStringIpv6ArrayRequest(
 		if req != nil {
 			e.ArrStart()
 			for _, elem := range req {
-				json.EncodeIP(e, elem)
+				json.EncodeIPv6(e, elem)
 			}
 			e.ArrEnd()
 		}
@@ -8278,7 +8278,7 @@ func encodeTestRequestStringIpv6ArrayArrayRequest(
 			for _, elem := range req {
 				e.ArrStart()
 				for _, elem := range elem {
-					json.EncodeIP(e, elem)
+					json.EncodeIPv6(e, elem)
 				}
 				e.ArrEnd()
 			}
diff --git a/examples/ex_test_format/oas_response_decoders_gen.go b/examples/ex_test_format/oas_response_decoders_gen.go
index d92ee875..0b190fb5 100644
--- a/examples/ex_test_format/oas_response_decoders_gen.go
+++ b/examples/ex_test_format/oas_response_decoders_gen.go
@@ -25376,7 +25376,7 @@ func decodeTestResponseStringIpv4Response(resp *http.Response) (res netip.Addr,
 			d := jx.DecodeBytes(b)
 			var response netip.Addr
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv4(d)
 				response = v
 				if err != nil {
 					return err
@@ -25417,7 +25417,7 @@ func decodeTestResponseStringIpv4ArrayResponse(resp *http.Response) (res []netip
 				response = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv4(d)
 					elem = v
 					if err != nil {
 						return err
@@ -25466,7 +25466,7 @@ func decodeTestResponseStringIpv4ArrayArrayResponse(resp *http.Response) (res []
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv4(d)
 						elemElem = v
 						if err != nil {
 							return err
@@ -25646,7 +25646,7 @@ func decodeTestResponseStringIpv6Response(resp *http.Response) (res netip.Addr,
 			d := jx.DecodeBytes(b)
 			var response netip.Addr
 			if err := func() error {
-				v, err := json.DecodeIP(d)
+				v, err := json.DecodeIPv6(d)
 				response = v
 				if err != nil {
 					return err
@@ -25687,7 +25687,7 @@ func decodeTestResponseStringIpv6ArrayResponse(resp *http.Response) (res []netip
 				response = make([]netip.Addr, 0)
 				if err := d.Arr(func(d *jx.Decoder) error {
 					var elem netip.Addr
-					v, err := json.DecodeIP(d)
+					v, err := json.DecodeIPv6(d)
 					elem = v
 					if err != nil {
 						return err
@@ -25736,7 +25736,7 @@ func decodeTestResponseStringIpv6ArrayArrayResponse(resp *http.Response) (res []
 					elem = make([]netip.Addr, 0)
 					if err := d.Arr(func(d *jx.Decoder) error {
 						var elemElem netip.Addr
-						v, err := json.DecodeIP(d)
+						v, err := json.DecodeIPv6(d)
 						elemElem = v
 						if err != nil {
 							return err
diff --git a/examples/ex_test_format/oas_response_encoders_gen.go b/examples/ex_test_format/oas_response_encoders_gen.go
index 3b435777..113854e5 100644
--- a/examples/ex_test_format/oas_response_encoders_gen.go
+++ b/examples/ex_test_format/oas_response_encoders_gen.go
@@ -9969,7 +9969,7 @@ func encodeTestResponseStringIpv4Response(response netip.Addr, w http.ResponseWr
 	span.SetStatus(codes.Ok, http.StatusText(200))
 	e := jx.GetEncoder()
 
-	json.EncodeIP(e, response)
+	json.EncodeIPv4(e, response)
 	if _, err := e.WriteTo(w); err != nil {
 		return errors.Wrap(err, "write")
 	}
@@ -9985,7 +9985,7 @@ func encodeTestResponseStringIpv4ArrayResponse(response []netip.Addr, w http.Res
 
 	e.ArrStart()
 	for _, elem := range response {
-		json.EncodeIP(e, elem)
+		json.EncodeIPv4(e, elem)
 	}
 	e.ArrEnd()
 	if _, err := e.WriteTo(w); err != nil {
@@ -10005,7 +10005,7 @@ func encodeTestResponseStringIpv4ArrayArrayResponse(response [][]netip.Addr, w h
 	for _, elem := range response {
 		e.ArrStart()
 		for _, elem := range elem {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv4(e, elem)
 		}
 		e.ArrEnd()
 	}
@@ -10077,7 +10077,7 @@ func encodeTestResponseStringIpv6Response(response netip.Addr, w http.ResponseWr
 	span.SetStatus(codes.Ok, http.StatusText(200))
 	e := jx.GetEncoder()
 
-	json.EncodeIP(e, response)
+	json.EncodeIPv6(e, response)
 	if _, err := e.WriteTo(w); err != nil {
 		return errors.Wrap(err, "write")
 	}
@@ -10093,7 +10093,7 @@ func encodeTestResponseStringIpv6ArrayResponse(response []netip.Addr, w http.Res
 
 	e.ArrStart()
 	for _, elem := range response {
-		json.EncodeIP(e, elem)
+		json.EncodeIPv6(e, elem)
 	}
 	e.ArrEnd()
 	if _, err := e.WriteTo(w); err != nil {
@@ -10113,7 +10113,7 @@ func encodeTestResponseStringIpv6ArrayArrayResponse(response [][]netip.Addr, w h
 	for _, elem := range response {
 		e.ArrStart()
 		for _, elem := range elem {
-			json.EncodeIP(e, elem)
+			json.EncodeIPv6(e, elem)
 		}
 		e.ArrEnd()
 	}
diff --git a/internal/integration/json_test.go b/internal/integration/json_test.go
index aa81a0e4..0e3c1f10 100644
--- a/internal/integration/json_test.go
+++ b/internal/integration/json_test.go
@@ -1109,3 +1109,21 @@ func TestJSONNull(t *testing.T) {
 		})
 	}
 }
+
+func TestJSONIP(t *testing.T) {
+	var (
+		ipv4 api.OptIPv4
+		ipv6 api.OptIPv6
+		ip   api.OptIP
+	)
+
+	a := require.New(t)
+	a.NoError(ipv4.Decode(jx.DecodeStr(`"1.1.1.1"`)))
+	a.Error(ipv4.Decode(jx.DecodeStr(`"2001:db8::68"`)))
+
+	a.NoError(ipv6.Decode(jx.DecodeStr(`"2001:db8::68"`)))
+	a.Error(ipv6.Decode(jx.DecodeStr(`"1.1.1.1"`)))
+
+	a.NoError(ip.Decode(jx.DecodeStr(`"2001:db8::68"`)))
+	a.NoError(ip.Decode(jx.DecodeStr(`"1.1.1.1"`)))
+}
diff --git a/json/ip_test.go b/json/ip_test.go
index 41b6f261..ab05ed63 100644
--- a/json/ip_test.go
+++ b/json/ip_test.go
@@ -1,10 +1,12 @@
 package json
 
 import (
+	"fmt"
 	"net/netip"
 	"testing"
 
 	"github.com/go-faster/jx"
+	"github.com/stretchr/testify/require"
 )
 
 func BenchmarkEncodeIP(b *testing.B) {
@@ -21,3 +23,121 @@ func BenchmarkEncodeIP(b *testing.B) {
 		EncodeIP(e, ip)
 	}
 }
+
+func BenchmarkDecodeIP(b *testing.B) {
+	data := []byte(`"127.0.0.1"`)
+	d := jx.GetDecoder()
+
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	var (
+		ip  netip.Addr
+		err error
+	)
+	for i := 0; i < b.N; i++ {
+		d.ResetBytes(data)
+		ip, err = DecodeIP(d)
+	}
+
+	if err != nil {
+		b.Fatal(err)
+	}
+	if !ip.Is4() {
+		b.Fatal("wrong ip version")
+	}
+}
+
+func TestIPv4(t *testing.T) {
+	addr := netip.MustParseAddr
+	tests := []struct {
+		input       string
+		want        netip.Addr
+		errContains string
+	}{
+		// Valid IPv4.
+		{`"1.1.1.1"`, addr("1.1.1.1"), ""},
+		{`"127.0.0.1"`, addr("127.0.0.1"), ""},
+
+		// Invalid IPv4.
+		{`"127.0.0"`, netip.Addr{}, "bad ip format"},
+		{`"127.0.0.-1"`, netip.Addr{}, "bad ip format"},
+		{`"127.256.0.0"`, netip.Addr{}, "bad ip format"},
+
+		// Wrong IP version.
+		{`"::1"`, netip.Addr{}, "wrong ip version"},
+		{`"2001:db8::1"`, netip.Addr{}, "wrong ip version"},
+
+		// Wrong type.
+		{`1`, netip.Addr{}, "start: unexpected byte"},
+		{`true`, netip.Addr{}, "start: unexpected byte"},
+		{`null`, netip.Addr{}, "start: unexpected byte"},
+	}
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+			a := require.New(t)
+
+			d := jx.DecodeStr(tt.input)
+			got, err := DecodeIPv4(d)
+			if tt.errContains != "" {
+				a.ErrorContains(err, tt.errContains)
+				return
+			}
+			a.NoError(err)
+			a.Equal(tt.want, got)
+
+			e := jx.GetEncoder()
+			EncodeIPv4(e, got)
+			got2, err := DecodeIPv4(jx.DecodeBytes(e.Bytes()))
+			a.NoError(err)
+			a.True(got.Compare(got2) == 0)
+		})
+	}
+}
+
+func TestIPv6(t *testing.T) {
+	addr := netip.MustParseAddr
+	tests := []struct {
+		input       string
+		want        netip.Addr
+		errContains string
+	}{
+		// Valid IPv6.
+		{`"::1"`, addr("::1"), ""},
+		{`"2001:db8::1"`, addr("2001:db8::1"), ""},
+
+		// Invalid IPv6.
+		{`":1"`, netip.Addr{}, "bad ip format"},
+		{`"::-1"`, netip.Addr{}, "bad ip format"},
+		{`"2001:hb8::1"`, netip.Addr{}, "bad ip format"},
+
+		// Wrong IP version.
+		{`"127.0.0.1"`, netip.Addr{}, "wrong ip version"},
+		{`"1.1.1.1"`, netip.Addr{}, "wrong ip version"},
+
+		// Wrong type.
+		{`1`, netip.Addr{}, "start: unexpected byte"},
+		{`true`, netip.Addr{}, "start: unexpected byte"},
+		{`null`, netip.Addr{}, "start: unexpected byte"},
+	}
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+			a := require.New(t)
+
+			d := jx.DecodeStr(tt.input)
+			got, err := DecodeIPv6(d)
+			if tt.errContains != "" {
+				a.ErrorContains(err, tt.errContains)
+				return
+			}
+			a.NoError(err)
+			a.Equal(tt.want, got)
+
+			e := jx.GetEncoder()
+			EncodeIPv6(e, got)
+			got2, err := DecodeIPv6(jx.DecodeBytes(e.Bytes()))
+			a.NoError(err)
+			a.True(got.Compare(got2) == 0)
+		})
+	}
+}