2016-08-03 5 views
4

Ich versuche, die Richtlinie für eine S3-Bucket auf AWS zu ändern. Ich habe die folgende JSON-Struktur für eine Richtlinie erstellt:Golan-Struktur für Json, um einen Wert optional ein Array zu ermöglichen

type Policy struct { 
    Version  string `json:"Version"` 
    Id   string `json:"Id"` 
    Statement []Statement `json:"Statement"` 
} 

type Statement struct { 
    Sid   string  `json:"Sid"` 
    Effect  string  `json:"Effect"` 
    Principal Principal `json:"Principal"` 
    Action  []string `json:"Action"` 
    Resource []string `json:"Resource"` 
} 

type Principal struct { 
    AWS[]string `json:"AWS"` 
} 

Das funktioniert gut für das Festlegen von Bucket-Richtlinien. Das Problem tritt auf, wenn ich versuche, die aktuelle Richtlinie abzurufen und sie zu ändern.

Wenn es eine Anweisung gibt, die nur einen AWS-, Aktions- oder Ressourcenwert hat, konvertiert Amazon sie von einem Array in einen einfachen Wert, was dazu führt, dass mein Unmarshalling fehlschlägt.

Gibt es eine Möglichkeit, dass ich AWS/Aktion/Ressource-Werte angeben kann, um entweder eine Zeichenfolge Slice oder nur eine Zeichenfolge?


Ich weiß, dass es Pakete zur Verfügung, die ich um diesen zu einem gewissen Grad (github.com/Jeffail/gabs, zum Beispiel) zu bekommen verwenden könnte, aber es wäre sauberer sein, nur die JSON-Struktur zu erstellen, da es ziemlich einfach ist.

+0

Können Sie Beispiele für beide JSON-Zeichenfolge bereitstellen? – huygn

+0

Übrigens sind all diese JSON-Tags unnötig, weil sie genau mit den Feldnamen übereinstimmen. Wenn es keine JSON-Tags auf dem Typ gibt, werden stattdessen die Feldnamen verwendet. – voutasaurus

+0

Ich wusste nicht, ob die JSON-Tags erforderlich waren, aber ich denke, sie werden nur benötigt, wenn Sie die Felder in der Implementierung anders benennen möchten. – arewm

Antwort

2

Als Alternative zu interface{} können Sie einen Typ namens MaybeSlice erstellen und benutzerdefinierte MarshalJSON- und UnmarshalJSON-Methoden darauf implementieren.

type MaybeSlice []string 

func (ms *MaybeSlice) MarshalJSON() ([]byte, error) { 
    // Use normal json.Marshal for subtypes 
    if len(*ms) == 1 { 
     return json.Marshal(([]string)(*ms)[0]) 
    } 
    return json.Marshal(*ms) 
} 

func (ms *MaybeSlice) UnmarshalJSON(data []byte) error { 
    // Use normal json.Unmarshal for subtypes 
    var s string 
    if err := json.Unmarshal(data, &s); err != nil { 
     var v []string 
     if err := json.Unmarshal(data, &v); err != nil { 
      return err 
     } 
     *ms = v 
     return nil 
    } 
    *ms = []string{s} 
    return nil 
} 

durch diese Verfahren die Umsetzung der MaybeSlice Art werden die Schnittstellen erfüllen erwartet von json.Marshal und json.Unmarshal so dass Sie nicht benutzerdefinierte Marshaller für alle Arten implementieren müssen, nur diese.

MaybeSlice ist ein schrecklicher Name, aber hoffentlich bekommen Sie die Idee.

Diese benutzerdefinierten Methoden sind einfacher mit Strukturtypen, aber ich denke, das obige ist richtig. Wenn ich mich richtig erinnere, müssen Sie Action eine *MaybeSlice machen, um das oben genannte zu verwenden.

+0

Beispiel mit dem oben genannten: https://play.golang.org/p/X5Xauqwoz8 – voutasaurus

+0

Arbeitete genau so, wie ich wollte, danke! Ich fing alleine an zu kommen, aber es hätte mich etwas länger gedauert. :) – arewm

1

Verwenden interface{} wenn der Typ des Feldes Sie ist nicht garantiert zu analysieren gehst:

type Statement struct { 
    Sid  string `json:"Sid"` 
    Effect string `json:"Effect"` 
    Principal Principal `json:"Principal"` 

    Action interface{} `json:"Action"` 
    Resource interface{} `json:"Resource"` 
} 

und die zugrunde liegenden ursprünglichen Datentyp Zugriff auf eine Art Schalter:

//Example: Trying to access Action member of a statement myStatement. 
switch a := myStatement.Action.(type) { 
    case []string: 
     //Action is a slice. Handle it accordingly. 
    case string: 
     //Action is a string. Handle it accordingly. 
    default: 
     //Some other datatype that can be returned by aws? 
} 

Oder Sie kann für beide Fälle separate Strukturen haben, wenn Unmarshaling fehlgeschlagen ist, Unmarshal es in die andere Struktur, etwa so:

err := json.Unmarshal(jsonStr, &struct1) 
if err != nil { 
    fmt.Println(err) 
    err = json.Unmarshal(jsonStr, &struct2) 
} 
Verwandte Themen