@@ -137,3 +137,93 @@ func ExampleReader_NetworksWithin() {
137137 // 1.0.64.0/18: Cable/DSL
138138 // 1.0.128.0/17: Cable/DSL
139139}
140+
141+ // CustomCity represents a simplified city record with custom unmarshaling.
142+ // This demonstrates the Unmarshaler interface for high-performance decoding.
143+ type CustomCity struct {
144+ Names map [string ]string
145+ GeoNameID uint
146+ }
147+
148+ // UnmarshalMaxMindDB implements the maxminddb.Unmarshaler interface.
149+ // This provides significant performance improvements over reflection-based decoding
150+ // by allowing custom, optimized decoding logic for performance-critical applications.
151+ func (c * CustomCity ) UnmarshalMaxMindDB (d * maxminddb.Decoder ) error {
152+ for key , err := range d .DecodeMap () {
153+ if err != nil {
154+ return err
155+ }
156+
157+ switch string (key ) {
158+ case "city" :
159+ // Decode nested city structure
160+ for cityKey , cityErr := range d .DecodeMap () {
161+ if cityErr != nil {
162+ return cityErr
163+ }
164+ switch string (cityKey ) {
165+ case "names" :
166+ // Decode nested map[string]string for localized names
167+ names := make (map [string ]string )
168+ for nameKey , nameErr := range d .DecodeMap () {
169+ if nameErr != nil {
170+ return nameErr
171+ }
172+ value , valueErr := d .DecodeString ()
173+ if valueErr != nil {
174+ return valueErr
175+ }
176+ names [string (nameKey )] = value
177+ }
178+ c .Names = names
179+ case "geoname_id" :
180+ geoID , err := d .DecodeUInt32 ()
181+ if err != nil {
182+ return err
183+ }
184+ c .GeoNameID = uint (geoID )
185+ default :
186+ if err := d .SkipValue (); err != nil {
187+ return err
188+ }
189+ }
190+ }
191+ default :
192+ // Skip unknown fields to ensure forward compatibility
193+ if err := d .SkipValue (); err != nil {
194+ return err
195+ }
196+ }
197+ }
198+ return nil
199+ }
200+
201+ // This example demonstrates how to use the Unmarshaler interface for high-performance
202+ // custom decoding. Types implementing Unmarshaler automatically use custom decoding
203+ // logic instead of reflection, providing better performance for critical applications.
204+ func ExampleUnmarshaler () {
205+ db , err := maxminddb .Open ("test-data/test-data/GeoIP2-City-Test.mmdb" )
206+ if err != nil {
207+ log .Fatal (err )
208+ }
209+ defer db .Close () //nolint:errcheck // error doesn't matter
210+
211+ addr := netip .MustParseAddr ("81.2.69.142" )
212+
213+ // CustomCity implements Unmarshaler, so it will automatically use
214+ // the custom UnmarshalMaxMindDB method instead of reflection
215+ var city CustomCity
216+ err = db .Lookup (addr ).Decode (& city )
217+ if err != nil {
218+ log .Panic (err )
219+ }
220+
221+ fmt .Printf ("City ID: %d\n " , city .GeoNameID )
222+ fmt .Printf ("English name: %s\n " , city .Names ["en" ])
223+ fmt .Printf ("German name: %s\n " , city .Names ["de" ])
224+
225+ // Output:
226+ // City ID: 2643743
227+ // English name: London
228+ // German name: London
229+ }
0 commit comments