Разбор JSONSchema для структурирования типа в golang - PullRequest
0 голосов
/ 06 июня 2018

Итак, мой вариант использования состоит в разборе изменяющихся схем JSON на новые типы структур, которые в дальнейшем будут использоваться с ORM для извлечения данных из базы данных SQL.Будучи скомпилированным по своей природе, я считаю, что не будет готового решения на ходу, но есть ли какой-нибудь хак для этого без создания отдельного процесса go.Я пытался с отражением, но не смог найти удовлетворительный подход.

В настоящее время я использую библиотеку ah generate , которая генерирует структуры, но я застрял в том, как загрузить эти новые структурывводит во время выполнения go.

EDIT

Пример схемы JSON:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Address",
  "id": "Address",
  "type": "object",
  "description": "address",
  "properties": {
    "houseName": {
      "type": "string",
      "description": "House Name",
      "maxLength": 30
    },
    "houseNumber": {
      "type": "string",
      "description": "House Number",
      "maxLength": 4
    },
    "flatNumber": {
      "type": "string",
      "description": "Flat",
      "maxLength": 15
    },
    "street": {
      "type": "string",
      "description": "Address 1",
      "maxLength": 40
    },
    "district": {
      "type": "string",
      "description": "Address 2",
      "maxLength": 30
    },
    "town": {
      "type": "string",
      "description": "City",
      "maxLength": 20
    },
    "county": {
      "type": "string",
      "description": "County",
      "maxLength": 20
    },
    "postcode": {
      "type": "string",
      "description": "Postcode",
      "maxLength": 8
    }
  }
}

Теперь в вышеупомянутой библиотеке есть командаИнструмент line, который генерирует текст для типа структуры для вышеупомянутого json, как показано ниже:

// Code generated by schema-generate. DO NOT EDIT.

package main

// Address address
type Address struct {
  County string `json:"county,omitempty"`
  District string `json:"district,omitempty"`
  FlatNumber string `json:"flatNumber,omitempty"`
  HouseName string `json:"houseName,omitempty"`
  HouseNumber string `json:"houseNumber,omitempty"`
  Postcode string `json:"postcode,omitempty"`
  Street string `json:"street,omitempty"`
  Town string `json:"town,omitempty"`
}

Теперь проблема заключается в том, как использовать этот тип структуры без повторной компиляции в программе.Есть хак, где я могу начать новый процесс go, но это не кажется хорошим способом сделать это.Еще один способ - написать собственный синтаксический анализатор для демаршаллинговой схемы JSON, что-то вроде:

b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
for k, v := range m {
    switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", vv)
    case float64:
        fmt.Println(k, "is float64", vv)
    case int:
        fmt.Println(k, "is int", vv)
    case []interface{}:
        fmt.Println(k, "is an array:")
        for i, u := range vv {
            fmt.Println(i, u)
        }
    default:
        fmt.Println(k, "is of a type I don't know how to handle")
    }
}

Может кто-нибудь предложить какие-нибудь указатели для поиска.Благодаря.

1 Ответ

0 голосов
/ 06 июня 2018

Похоже, вы пытаетесь реализовать свой собственный маршаллинг json.Это не важно: стандартный пакет json уже поддерживает это.Просто попросите ваш тип реализовать функции MarshalJSON и UnmarshalJSON (см. Первый пример на документах ).Предполагая, что некоторые поля будут общими (например, схема, идентификатор, тип), вы можете создать унифицированный тип, подобный следующему:

// poor naming, but we need this level of wrapping here
type Data struct {
    Metadata
}

type Metadata struct {
    Schema string `json:"$schema"`
    Type string `json:"type"`
    Description string `json:"description"`
    Id string `json:"id"`
    Properties json.RawMessage `json:"properties"`
    Address *Address `json:"-"`
    // other types go here, too
}

Теперь все свойства будут перенесены в поле json.RawMessage (по сути это[]byte поле).То, что вы можете сделать в своей пользовательской функции unmarshall, теперь выглядит примерно так:

func (d *Data) UnmarshalJSON(b []byte) error {
    meta := Metadata{}
    // unmarshall common fields
    if err := json.Unmarshal(b, &meta); err != nil {
        return err
    }
    // Assuming the Type field contains the value that allows you to determine what data you're actually unmarshalling
    switch meta.Type {
    case "address":
        meta.Address = &Address{} // initialise field
        if err := json.Unmarshal([]byte(meta.Properties), meta.Address); err != nil {
            return err
        }
    case "name":
        meta.Name = &Name{}
        if err := json.Unmarshal([]byte(meta.Properties), meta.Name); err != nil {
            return err
        }
    default:
        return errors.New("unknown message type")
    }
    // all done
    d.Metadata = meta // assign to embedded
    // optionally: clean up the Properties field, as it contains raw JSON, and is exported
    d.Metadata.Properties = json.RawMessage{}
    return nil
}

Вы можете сделать почти то же самое для маршаллинга.Сначала выясните, с каким типом вы на самом деле работаете, затем поместите этот объект в поле свойств и , затем маршал всей структуры

func (d Data) MarshalJSON() ([]byte, error) {
    var (
        prop []byte
        err error
    )
    switch {
    case d.Metadata.Address != nil:
        prop, err = json.Marshal(d.Address)
    case d.Metadata.Name != nil:
        prop, err = json.Marshal(d.Name) // will only work if field isn't masked, better to be explicit
    default:
        err = errors.New("No properties to marshal") // handle in whatever way is best
    }
    if err != nil {
        return nil, err
    }
    d.Metadata.Properties = json.RawMessage(prop)
    return json.Marshal(d.Metadata) // marshal the unified type here
}
...