适合解析geojson的结构类型

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

Suitable struct type for unmarshal of geojson

问题

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

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

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

type GeojsonType struct {
    Type string
    Coordinates interface{}
}

请参考此链接获取完整示例:
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:

var jsonBlobPointString = []byte(`{"Type":"Point", "Coordinates":[1.1,2.0]}`)
var jsonBlobLineString = []byte(`{"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.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:

type GeojsonType struct {
    Type string
    Coordinates interface{}
}

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解码,并使用一些预处理像这样

package main

import (
	"encoding/json"
	"fmt"
)

type Point struct {
	Coordinates []float64
}

type Line struct {
	Points [][]float64
}

type Polygon struct {
	Lines [][][]float64
}

type GeojsonType struct {
	Type        string
	Coordinates json.RawMessage
	Point       Point
	Line        Line
	Polygon     Polygon
}

var jsonBlob = []byte(`[
{"Type":"Point", "Coordinates":[1.1,2.0]},
{"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]},
{"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}
]`)

func main() {
	var geojsonPoints []GeojsonType
	err := json.Unmarshal(jsonBlob, &geojsonPoints)
	if err != nil {
		fmt.Println("error:", err)
	}

	// 后处理坐标

	for i := range geojsonPoints {
		t := &geojsonPoints[i]

		switch t.Type {
		case "Point":
			err = json.Unmarshal(t.Coordinates, &t.Point.Coordinates)
		case "LineString":
			err = json.Unmarshal(t.Coordinates, &t.Line.Points)
		case "Polygon":
			err = json.Unmarshal(t.Coordinates, &t.Polygon.Lines)
		default:
			panic("Unknown type")
		}
		if err != nil {
			fmt.Printf("Failed to convert %s: %s", t.Type, err)
		}
		fmt.Printf("%+v\n", t)
	}
}

打印结果为

&{Type:Point Coordinates:[91 49 46 49 44 50 46 48 93] Point:{Coordinates:[1.1 2]} Line:{Points:[]} Polygon:{Lines:[]}}
&{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:[]}}
&{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

package main

import (
	"encoding/json"
	"fmt"
)

type Point struct {
	Coordinates []float64
}

type Line struct {
	Points [][]float64
}

type Polygon struct {
	Lines [][][]float64
}

type GeojsonType struct {
	Type        string
	Coordinates json.RawMessage
	Point       Point
	Line        Line
	Polygon     Polygon
}

var jsonBlob = []byte(`[
{"Type":"Point", "Coordinates":[1.1,2.0]},
{"Type":"LineString", "Coordinates":[[1.1,2.0],[3.0,6.3]]},
{"Type":"Polygon", "Coordinates":[[[1.1,2.0],[3.0,6.3],[5.1,7.0],[1.1,2.0]]]}
]`)

func main() {
	var geojsonPoints []GeojsonType
	err := json.Unmarshal(jsonBlob, &geojsonPoints)
	if err != nil {
		fmt.Println("error:", err)
	}

	// Postprocess the coordinates	

	for i := range geojsonPoints {
		t := &geojsonPoints[i]

		switch t.Type {
		case "Point":
			err = json.Unmarshal(t.Coordinates, &t.Point.Coordinates)
		case "LineString":
			err = json.Unmarshal(t.Coordinates, &t.Line.Points)
		case "Polygon":
			err = json.Unmarshal(t.Coordinates, &t.Polygon.Lines)
		default:
			panic("Unknown type")
		}
		if err != nil {
			fmt.Printf("Failed to convert %s: %s", t.Type, err)
		}
		fmt.Printf("%+v\n", t)
	}
}

Which prints

&{Type:Point Coordinates:[91 49 46 49 44 50 46 48 93] Point:{Coordinates:[1.1 2]} Line:{Points:[]} Polygon:{Lines:[]}}
&{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:[]}}
&{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函数。

package geojson

//https://stackoverflow.com/questions/15719532/suitable-struct-type-for-unmarshal-of-geojson

import (
	"encoding/json"
)

type Point struct {
	Coordinates []float64
}

type Line struct {
	Points [][]float64
}

type Polygon struct {
	Lines [][][]float64
}

type Geojson struct {
	Type        string          `json:"type"`
	Coordinates json.RawMessage `json:"coordinates"`
	Point       Point           `json:"-"`
	Line        Line            `json:"-"`
	Polygon     Polygon         `json:"-"`
}

func (g *Geojson) UnmarshalJSON(b []byte) error {

	type Alias Geojson
	aux := (*Alias)(g)

	err := json.Unmarshal(b, &aux)

	if err != nil {
		return err
	}

	switch g.Type {
	case "Point":
		err = json.Unmarshal(g.Coordinates, &g.Point.Coordinates)
	case "LineString":
		err = json.Unmarshal(g.Coordinates, &g.Line.Points)
	case "Polygon":
		err = json.Unmarshal(g.Coordinates, &g.Polygon.Lines)
	}

	g.Coordinates = []byte(nil)

	return err
}

func (g Geojson) MarshalJSON() ([]byte, error) {

	var raw json.RawMessage
	var err error

	switch g.Type {
	case "Point":
		raw, err = json.Marshal(&g.Point.Coordinates)
	case "LineString":
		raw, err = json.Marshal(&g.Line.Points)
	case "Polygon":
		raw, err = json.Marshal(&g.Polygon.Lines)
	}

	if err != nil {
		return nil, err
	}

	g.Coordinates = raw

	type Alias Geojson
	aux := (*Alias)(&g)
	return json.Marshal(aux)
}
英文:

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

package geojson

//https://stackoverflow.com/questions/15719532/suitable-struct-type-for-unmarshal-of-geojson

import (
	"encoding/json"
)

type Point struct {
	Coordinates []float64
}

type Line struct {
	Points [][]float64
}

type Polygon struct {
	Lines [][][]float64
}

type Geojson struct {
	Type        string          `json:"type"`
	Coordinates json.RawMessage `json:"coordinates"`
	Point       Point           `json:"-"`
	Line        Line            `json:"-"`
	Polygon     Polygon         `json:"-"`
}

func (g *Geojson) UnmarshalJSON(b []byte) error {

	type Alias Geojson
	aux := (*Alias)(g)

	err := json.Unmarshal(b, &aux)

	if err != nil {
		return err
	}

	switch g.Type {
	case "Point":
		err = json.Unmarshal(g.Coordinates, &g.Point.Coordinates)
	case "LineString":
		err = json.Unmarshal(g.Coordinates, &g.Line.Points)
	case "Polygon":
		err = json.Unmarshal(g.Coordinates, &g.Polygon.Lines)
	}

	g.Coordinates = []byte(nil)

	return err
}

func (g Geojson) MarshalJSON() ([]byte, error) {

	var raw json.RawMessage
	var err error

	switch g.Type {
	case "Point":
		raw, err = json.Marshal(&g.Point.Coordinates)
	case "LineString":
		raw, err = json.Marshal(&g.Line.Points)
	case "Polygon":
		raw, err = json.Marshal(&g.Polygon.Lines)
	}

	if err != nil {
		return nil, err
	}

	g.Coordinates = raw

	type Alias Geojson
	aux := (*Alias)(&g)
	return json.Marshal(aux)
}

答案3

得分: 0

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

package geojson

import (
	"encoding/json"
)

type PointCoordinates []float64              // 点
type LineCoordinates [][]float64             // 1. 多点,2. 线
type PolygonCoordinates [][][]float64        // 1. 多线,2. 多边形
type MultiPolygonCoordinates [][][][]float64 // 1. 多多边形

type Point struct {
	PointCoords PointCoordinates
}

type MultiPoint struct {
	MultiPointCoords LineCoordinates
}

type Line struct {
	LineCoords LineCoordinates
}

type MultiLine struct {
	MultiLineCoords PolygonCoordinates
}

type Polygon struct {
	PolygonCoords PolygonCoordinates
}

type MultiPolygon struct {
	MultiPolygonCoords MultiPolygonCoordinates
}

type Geojson struct {
	Type         string          `json:"type"`
	Coordinates  json.RawMessage `json:"coordinates"`
	Point        Point           `json:"-"`
	MultiPoint   MultiPoint      `json:"-"`
	Line         Line            `json:"-"`
	MultiLine    MultiLine       `json:"-"`
	Polygon      Polygon         `json:"-"`
	MultiPolygon MultiPolygon    `json:"-"`
}

func (g *Geojson) UnmarshalJSON(b []byte) error {

	type Alias Geojson
	aux := (*Alias)(g)

	err := json.Unmarshal(b, &aux)

	if err != nil {
		return err
	}

	switch g.Type {
	case "Point":
		err = json.Unmarshal(g.Coordinates, &g.Point.PointCoords)
	case "MultiPoint":
		err = json.Unmarshal(g.Coordinates, &g.MultiPoint.MultiPointCoords)
	case "LineString":
		err = json.Unmarshal(g.Coordinates, &g.Line.LineCoords)
	case "MultiLineString":
		err = json.Unmarshal(g.Coordinates, &g.MultiLine.MultiLineCoords)
	case "Polygon":
		err = json.Unmarshal(g.Coordinates, &g.Polygon.PolygonCoords)
	case "MultiPolygon":
		err = json.Unmarshal(g.Coordinates, &g.MultiPolygon.MultiPolygonCoords)
	}

	g.Coordinates = []byte(nil)

	return err
}

func (g Geojson) MarshalJSON() ([]byte, error) {

	var raw json.RawMessage
	var err error

	switch g.Type {
	case "Point":
		raw, err = json.Marshal(&g.Point.PointCoords)
	case "MultiPoint":
		raw, err = json.Marshal(&g.MultiPoint.MultiPointCoords)
	case "LineString":
		raw, err = json.Marshal(&g.Line.LineCoords)
	case "MultiLineString":
		raw, err = json.Marshal(&g.MultiLine.MultiLineCoords)
	case "Polygon":
		raw, err = json.Marshal(&g.Polygon.PolygonCoords)
	case "MultiPolygon":
		raw, err = json.Marshal(&g.MultiPolygon.MultiPolygonCoords)
	}

	if err != nil {
		return nil, err
	}

	g.Coordinates = raw

	type Alias Geojson
	aux := (*Alias)(&g)
	return json.Marshal(aux)
}
英文:

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

package geojson
import (
"encoding/json"
)
type PointCoordinates []float64              // Point
type LineCoordinates [][]float64             // 1. Multipoint, 2. LineString
type PolygonCoordinates [][][]float64        // 1. MultiLineString, 2. Polygon
type MultiPolygonCoordinates [][][][]float64 // 1. Multi Polygon
type Point struct {
PointCoords PointCoordinates
}
type MultiPoint struct {
MultiPointCoords LineCoordinates
}
type Line struct {
LineCoords LineCoordinates
}
type MultiLine struct {
MultiLineCoords PolygonCoordinates
}
type Polygon struct {
PolygonCoords PolygonCoordinates
}
type MultiPolygon struct {
MultiPolygonCoords MultiPolygonCoordinates
}
type Geojson struct {
Type         string          `json:"type"`
Coordinates  json.RawMessage `json:"coordinates"`
Point        Point           `json:"-"`
MultiPoint   MultiPoint      `json:"-"`
Line         Line            `json:"-"`
MultiLine    MultiLine       `json:"-"`
Polygon      Polygon         `json:"-"`
MultiPolygon MultiPolygon    `json:"-"`
}
func (g *Geojson) UnmarshalJSON(b []byte) error {
type Alias Geojson
aux := (*Alias)(g)
err := json.Unmarshal(b, &aux)
if err != nil {
return err
}
switch g.Type {
case "Point":
err = json.Unmarshal(g.Coordinates, &g.Point.PointCoords)
case "MultiPoint":
err = json.Unmarshal(g.Coordinates, &g.MultiPoint.MultiPointCoords)
case "LineString":
err = json.Unmarshal(g.Coordinates, &g.Line.LineCoords)
case "MultiLineString":
err = json.Unmarshal(g.Coordinates, &g.MultiLine.MultiLineCoords)
case "Polygon":
err = json.Unmarshal(g.Coordinates, &g.Polygon.PolygonCoords)
case "MultiPolygon":
err = json.Unmarshal(g.Coordinates, &g.MultiPolygon.MultiPolygonCoords)
}
g.Coordinates = []byte(nil)
return err
}
func (g Geojson) MarshalJSON() ([]byte, error) {
var raw json.RawMessage
var err error
switch g.Type {
case "Point":
raw, err = json.Marshal(&g.Point.PointCoords)
case "MultiPoint":
raw, err = json.Marshal(&g.MultiPoint.MultiPointCoords)
case "LineString":
raw, err = json.Marshal(&g.Line.LineCoords)
case "MultiLineString":
raw, err = json.Marshal(&g.MultiLine.MultiLineCoords)
case "Polygon":
raw, err = json.Marshal(&g.Polygon.PolygonCoords)
case "MultiPolygon":
raw, err = json.Marshal(&g.MultiPolygon.MultiPolygonCoords)
}
if err != nil {
return nil, err
}
g.Coordinates = raw
type Alias Geojson
aux := (*Alias)(&g)
return json.Marshal(aux)
}

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:

确定