适合解析geojson的结构类型

huangapple go评论126阅读模式
英文:

Suitable struct type for unmarshal of geojson

问题

我想将一个geojson字符串解组成适当的结构类型。
我有三个不同的geojson字符串,我想将它们解组成相同的结构体:

  1. var jsonBlobPointString = []byte(`{"Type":"Point", "Coordinates":[1.1,2.0]}`)
  2. var jsonBlobLineString = []byte(`{"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]}`)
  3. var jsonBlobPolygonString = []byte(`{"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}`)

我想出了一个结构类型,但我对它不完全满意:

  1. type GeojsonType struct {
  2. Type string
  3. Coordinates interface{}
  4. }

请参考此链接获取完整示例:
http://play.golang.org/p/Bt-51BX__A

我不想使用interface{}作为Coordinates的类型。
我希望使用一些可以为Point提供一些验证的东西,例如Coordinates []float64,
以及LineString的Coordinates [][]float64。

是否可能创建一个结构类型,使得Point、LineString和Polygon都可以在Coordinates中表示,而不使用interface?

英文:

I want to unmarshal a geojson string into a suitable struct type.
I have three different geojson strings that I want to unmarshal into the same struct:

  1. var jsonBlobPointString = []byte(`{"Type":"Point", "Coordinates":[1.1,2.0]}`)
  2. var jsonBlobLineString = []byte(`{"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]}`)
  3. var jsonBlobPolygonString = []byte(`{"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}`)

I came up with a struct type that I´m not totally happy with:

  1. type GeojsonType struct {
  2. Type string
  3. Coordinates interface{}
  4. }

See this link for complete example:
http://play.golang.org/p/Bt-51BX__A

I would rather not use interface{} for Coordinates.
I would instead use somehting that give me some validation for example Coordinates [] float64 for Point
and Coordinates[][] float64 for LineString.

Is it possible to create a struct type so that Point, LineString and Polygon all can be represented in Coordinates without using interface?

答案1

得分: 9

你想要的是从同一个json字典中创建3种不同类型的对象。

据我所知,这是不可能的,但是你可以使用RawMessage类型来延迟json解码,并使用一些预处理像这样

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Point struct {
  7. Coordinates []float64
  8. }
  9. type Line struct {
  10. Points [][]float64
  11. }
  12. type Polygon struct {
  13. Lines [][][]float64
  14. }
  15. type GeojsonType struct {
  16. Type string
  17. Coordinates json.RawMessage
  18. Point Point
  19. Line Line
  20. Polygon Polygon
  21. }
  22. var jsonBlob = []byte(`[
  23. {"Type":"Point", "Coordinates":[1.1,2.0]},
  24. {"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]},
  25. {"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}
  26. ]`)
  27. func main() {
  28. var geojsonPoints []GeojsonType
  29. err := json.Unmarshal(jsonBlob, &geojsonPoints)
  30. if err != nil {
  31. fmt.Println("error:", err)
  32. }
  33. // 后处理坐标
  34. for i := range geojsonPoints {
  35. t := &geojsonPoints[i]
  36. switch t.Type {
  37. case "Point":
  38. err = json.Unmarshal(t.Coordinates, &t.Point.Coordinates)
  39. case "LineString":
  40. err = json.Unmarshal(t.Coordinates, &t.Line.Points)
  41. case "Polygon":
  42. err = json.Unmarshal(t.Coordinates, &t.Polygon.Lines)
  43. default:
  44. panic("Unknown type")
  45. }
  46. if err != nil {
  47. fmt.Printf("Failed to convert %s: %s", t.Type, err)
  48. }
  49. fmt.Printf("%+v\n", t)
  50. }
  51. }

打印结果为

  1. &{Type:Point Coordinates:[91 49 46 49 44 50 46 48 93] Point:{Coordinates:[1.1 2]} Line:{Points:[]} Polygon:{Lines:[]}}
  2. &{Type:LineString Coordinates:[91 91 49 46 49 44 50 46 48 93 44 91 51 46 48 44 54 46 51 93 93] Point:{Coordinates:[]} Line:{Points:[[1.1 2] [3 6.3]]} Polygon:{Lines:[]}}
  3. &{Type:Polygon Coordinates:[91 91 91 49 46 49 44 50 46 48 93 44 91 51 46 48 44 54 46 51 93 44 91 53 46 49 44 55 46 48 93 44 91 49 46 49 44 50 46 48 93 93 93] Point:{Coordinates:[]} Line:{Points:[]} Polygon:{Lines:[[[1.1 2] [3 6.3] [5.1 7] [1.1 2]]]}}
英文:

What you want is to create 3 different types of object from the same json dictionary.

As far as I know that isn't possible, however you can use the RawMessage type to delay the json decoding and use a bit of pre-processing like this

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Point struct {
  7. Coordinates []float64
  8. }
  9. type Line struct {
  10. Points [][]float64
  11. }
  12. type Polygon struct {
  13. Lines [][][]float64
  14. }
  15. type GeojsonType struct {
  16. Type string
  17. Coordinates json.RawMessage
  18. Point Point
  19. Line Line
  20. Polygon Polygon
  21. }
  22. var jsonBlob = []byte(`[
  23. {"Type":"Point", "Coordinates":[1.1,2.0]},
  24. {"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]},
  25. {"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}
  26. ]`)
  27. func main() {
  28. var geojsonPoints []GeojsonType
  29. err := json.Unmarshal(jsonBlob, &geojsonPoints)
  30. if err != nil {
  31. fmt.Println("error:", err)
  32. }
  33. // Postprocess the coordinates
  34. for i := range geojsonPoints {
  35. t := &geojsonPoints[i]
  36. switch t.Type {
  37. case "Point":
  38. err = json.Unmarshal(t.Coordinates, &t.Point.Coordinates)
  39. case "LineString":
  40. err = json.Unmarshal(t.Coordinates, &t.Line.Points)
  41. case "Polygon":
  42. err = json.Unmarshal(t.Coordinates, &t.Polygon.Lines)
  43. default:
  44. panic("Unknown type")
  45. }
  46. if err != nil {
  47. fmt.Printf("Failed to convert %s: %s", t.Type, err)
  48. }
  49. fmt.Printf("%+v\n", t)
  50. }
  51. }

Which prints

  1. &{Type:Point Coordinates:[91 49 46 49 44 50 46 48 93] Point:{Coordinates:[1.1 2]} Line:{Points:[]} Polygon:{Lines:[]}}
  2. &{Type:LineString Coordinates:[91 91 49 46 49 44 50 46 48 93 44 91 51 46 48 44 54 46 51 93 93] Point:{Coordinates:[]} Line:{Points:[[1.1 2] [3 6.3]]} Polygon:{Lines:[]}}
  3. &{Type:Polygon Coordinates:[91 91 91 49 46 49 44 50 46 48 93 44 91 51 46 48 44 54 46 51 93 44 91 53 46 49 44 55 46 48 93 44 91 49 46 49 44 50 46 48 93 93 93] Point:{Coordinates:[]} Line:{Points:[]} Polygon:{Lines:[[[1.1 2] [3 6.3] [5.1 7] [1.1 2]]]}}

答案2

得分: 3

基于Nick Craig-Wood的答案,我构建了以下的Marshal/UnMarshal函数。

  1. package geojson
  2. //https://stackoverflow.com/questions/15719532/suitable-struct-type-for-unmarshal-of-geojson
  3. import (
  4. "encoding/json"
  5. )
  6. type Point struct {
  7. Coordinates []float64
  8. }
  9. type Line struct {
  10. Points [][]float64
  11. }
  12. type Polygon struct {
  13. Lines [][][]float64
  14. }
  15. type Geojson struct {
  16. Type string `json:"type"`
  17. Coordinates json.RawMessage `json:"coordinates"`
  18. Point Point `json:"-"`
  19. Line Line `json:"-"`
  20. Polygon Polygon `json:"-"`
  21. }
  22. func (g *Geojson) UnmarshalJSON(b []byte) error {
  23. type Alias Geojson
  24. aux := (*Alias)(g)
  25. err := json.Unmarshal(b, &aux)
  26. if err != nil {
  27. return err
  28. }
  29. switch g.Type {
  30. case "Point":
  31. err = json.Unmarshal(g.Coordinates, &g.Point.Coordinates)
  32. case "LineString":
  33. err = json.Unmarshal(g.Coordinates, &g.Line.Points)
  34. case "Polygon":
  35. err = json.Unmarshal(g.Coordinates, &g.Polygon.Lines)
  36. }
  37. g.Coordinates = []byte(nil)
  38. return err
  39. }
  40. func (g Geojson) MarshalJSON() ([]byte, error) {
  41. var raw json.RawMessage
  42. var err error
  43. switch g.Type {
  44. case "Point":
  45. raw, err = json.Marshal(&g.Point.Coordinates)
  46. case "LineString":
  47. raw, err = json.Marshal(&g.Line.Points)
  48. case "Polygon":
  49. raw, err = json.Marshal(&g.Polygon.Lines)
  50. }
  51. if err != nil {
  52. return nil, err
  53. }
  54. g.Coordinates = raw
  55. type Alias Geojson
  56. aux := (*Alias)(&g)
  57. return json.Marshal(aux)
  58. }
英文:

Based on Nick Craig-Wood answer I built the following Marshal/UnMarshal functions

  1. package geojson
  2. //https://stackoverflow.com/questions/15719532/suitable-struct-type-for-unmarshal-of-geojson
  3. import (
  4. "encoding/json"
  5. )
  6. type Point struct {
  7. Coordinates []float64
  8. }
  9. type Line struct {
  10. Points [][]float64
  11. }
  12. type Polygon struct {
  13. Lines [][][]float64
  14. }
  15. type Geojson struct {
  16. Type string `json:"type"`
  17. Coordinates json.RawMessage `json:"coordinates"`
  18. Point Point `json:"-"`
  19. Line Line `json:"-"`
  20. Polygon Polygon `json:"-"`
  21. }
  22. func (g *Geojson) UnmarshalJSON(b []byte) error {
  23. type Alias Geojson
  24. aux := (*Alias)(g)
  25. err := json.Unmarshal(b, &aux)
  26. if err != nil {
  27. return err
  28. }
  29. switch g.Type {
  30. case "Point":
  31. err = json.Unmarshal(g.Coordinates, &g.Point.Coordinates)
  32. case "LineString":
  33. err = json.Unmarshal(g.Coordinates, &g.Line.Points)
  34. case "Polygon":
  35. err = json.Unmarshal(g.Coordinates, &g.Polygon.Lines)
  36. }
  37. g.Coordinates = []byte(nil)
  38. return err
  39. }
  40. func (g Geojson) MarshalJSON() ([]byte, error) {
  41. var raw json.RawMessage
  42. var err error
  43. switch g.Type {
  44. case "Point":
  45. raw, err = json.Marshal(&g.Point.Coordinates)
  46. case "LineString":
  47. raw, err = json.Marshal(&g.Line.Points)
  48. case "Polygon":
  49. raw, err = json.Marshal(&g.Polygon.Lines)
  50. }
  51. if err != nil {
  52. return nil, err
  53. }
  54. g.Coordinates = raw
  55. type Alias Geojson
  56. aux := (*Alias)(&g)
  57. return json.Marshal(aux)
  58. }

答案3

得分: 0

根据Madu Alikor的答案,为我的用例完成了它。

  1. package geojson
  2. import (
  3. "encoding/json"
  4. )
  5. type PointCoordinates []float64 // 点
  6. type LineCoordinates [][]float64 // 1. 多点,2. 线
  7. type PolygonCoordinates [][][]float64 // 1. 多线,2. 多边形
  8. type MultiPolygonCoordinates [][][][]float64 // 1. 多多边形
  9. type Point struct {
  10. PointCoords PointCoordinates
  11. }
  12. type MultiPoint struct {
  13. MultiPointCoords LineCoordinates
  14. }
  15. type Line struct {
  16. LineCoords LineCoordinates
  17. }
  18. type MultiLine struct {
  19. MultiLineCoords PolygonCoordinates
  20. }
  21. type Polygon struct {
  22. PolygonCoords PolygonCoordinates
  23. }
  24. type MultiPolygon struct {
  25. MultiPolygonCoords MultiPolygonCoordinates
  26. }
  27. type Geojson struct {
  28. Type string `json:"type"`
  29. Coordinates json.RawMessage `json:"coordinates"`
  30. Point Point `json:"-"`
  31. MultiPoint MultiPoint `json:"-"`
  32. Line Line `json:"-"`
  33. MultiLine MultiLine `json:"-"`
  34. Polygon Polygon `json:"-"`
  35. MultiPolygon MultiPolygon `json:"-"`
  36. }
  37. func (g *Geojson) UnmarshalJSON(b []byte) error {
  38. type Alias Geojson
  39. aux := (*Alias)(g)
  40. err := json.Unmarshal(b, &aux)
  41. if err != nil {
  42. return err
  43. }
  44. switch g.Type {
  45. case "Point":
  46. err = json.Unmarshal(g.Coordinates, &g.Point.PointCoords)
  47. case "MultiPoint":
  48. err = json.Unmarshal(g.Coordinates, &g.MultiPoint.MultiPointCoords)
  49. case "LineString":
  50. err = json.Unmarshal(g.Coordinates, &g.Line.LineCoords)
  51. case "MultiLineString":
  52. err = json.Unmarshal(g.Coordinates, &g.MultiLine.MultiLineCoords)
  53. case "Polygon":
  54. err = json.Unmarshal(g.Coordinates, &g.Polygon.PolygonCoords)
  55. case "MultiPolygon":
  56. err = json.Unmarshal(g.Coordinates, &g.MultiPolygon.MultiPolygonCoords)
  57. }
  58. g.Coordinates = []byte(nil)
  59. return err
  60. }
  61. func (g Geojson) MarshalJSON() ([]byte, error) {
  62. var raw json.RawMessage
  63. var err error
  64. switch g.Type {
  65. case "Point":
  66. raw, err = json.Marshal(&g.Point.PointCoords)
  67. case "MultiPoint":
  68. raw, err = json.Marshal(&g.MultiPoint.MultiPointCoords)
  69. case "LineString":
  70. raw, err = json.Marshal(&g.Line.LineCoords)
  71. case "MultiLineString":
  72. raw, err = json.Marshal(&g.MultiLine.MultiLineCoords)
  73. case "Polygon":
  74. raw, err = json.Marshal(&g.Polygon.PolygonCoords)
  75. case "MultiPolygon":
  76. raw, err = json.Marshal(&g.MultiPolygon.MultiPolygonCoords)
  77. }
  78. if err != nil {
  79. return nil, err
  80. }
  81. g.Coordinates = raw
  82. type Alias Geojson
  83. aux := (*Alias)(&g)
  84. return json.Marshal(aux)
  85. }
英文:

Based on Madu Alikor answer, completed it for my use case.

  1. package geojson
  2. import (
  3. "encoding/json"
  4. )
  5. type PointCoordinates []float64 // Point
  6. type LineCoordinates [][]float64 // 1. Multipoint, 2. LineString
  7. type PolygonCoordinates [][][]float64 // 1. MultiLineString, 2. Polygon
  8. type MultiPolygonCoordinates [][][][]float64 // 1. Multi Polygon
  9. type Point struct {
  10. PointCoords PointCoordinates
  11. }
  12. type MultiPoint struct {
  13. MultiPointCoords LineCoordinates
  14. }
  15. type Line struct {
  16. LineCoords LineCoordinates
  17. }
  18. type MultiLine struct {
  19. MultiLineCoords PolygonCoordinates
  20. }
  21. type Polygon struct {
  22. PolygonCoords PolygonCoordinates
  23. }
  24. type MultiPolygon struct {
  25. MultiPolygonCoords MultiPolygonCoordinates
  26. }
  27. type Geojson struct {
  28. Type string `json:"type"`
  29. Coordinates json.RawMessage `json:"coordinates"`
  30. Point Point `json:"-"`
  31. MultiPoint MultiPoint `json:"-"`
  32. Line Line `json:"-"`
  33. MultiLine MultiLine `json:"-"`
  34. Polygon Polygon `json:"-"`
  35. MultiPolygon MultiPolygon `json:"-"`
  36. }
  37. func (g *Geojson) UnmarshalJSON(b []byte) error {
  38. type Alias Geojson
  39. aux := (*Alias)(g)
  40. err := json.Unmarshal(b, &aux)
  41. if err != nil {
  42. return err
  43. }
  44. switch g.Type {
  45. case "Point":
  46. err = json.Unmarshal(g.Coordinates, &g.Point.PointCoords)
  47. case "MultiPoint":
  48. err = json.Unmarshal(g.Coordinates, &g.MultiPoint.MultiPointCoords)
  49. case "LineString":
  50. err = json.Unmarshal(g.Coordinates, &g.Line.LineCoords)
  51. case "MultiLineString":
  52. err = json.Unmarshal(g.Coordinates, &g.MultiLine.MultiLineCoords)
  53. case "Polygon":
  54. err = json.Unmarshal(g.Coordinates, &g.Polygon.PolygonCoords)
  55. case "MultiPolygon":
  56. err = json.Unmarshal(g.Coordinates, &g.MultiPolygon.MultiPolygonCoords)
  57. }
  58. g.Coordinates = []byte(nil)
  59. return err
  60. }
  61. func (g Geojson) MarshalJSON() ([]byte, error) {
  62. var raw json.RawMessage
  63. var err error
  64. switch g.Type {
  65. case "Point":
  66. raw, err = json.Marshal(&g.Point.PointCoords)
  67. case "MultiPoint":
  68. raw, err = json.Marshal(&g.MultiPoint.MultiPointCoords)
  69. case "LineString":
  70. raw, err = json.Marshal(&g.Line.LineCoords)
  71. case "MultiLineString":
  72. raw, err = json.Marshal(&g.MultiLine.MultiLineCoords)
  73. case "Polygon":
  74. raw, err = json.Marshal(&g.Polygon.PolygonCoords)
  75. case "MultiPolygon":
  76. raw, err = json.Marshal(&g.MultiPolygon.MultiPolygonCoords)
  77. }
  78. if err != nil {
  79. return nil, err
  80. }
  81. g.Coordinates = raw
  82. type Alias Geojson
  83. aux := (*Alias)(&g)
  84. return json.Marshal(aux)
  85. }

huangapple
  • 本文由 发表于 2013年3月30日 22:20:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/15719532.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定