Показаны сообщения с ярлыком Пакет xml в Golang. Показать все сообщения
Показаны сообщения с ярлыком Пакет xml в Golang. Показать все сообщения

воскресенье, 28 июня 2020 г.

Пакет xml в Golang - интерфейс Unmarshaler

Unmarshaler - это интерфейс, реализуемый объектами, которые могут демаршалировать описание своего XML элемента.

type Unmarshaler interface {
    UnmarshalXML(d *Decoder, start StartElement) error
}

UnmarshalXML декодирует один элемент XML, начиная с данного start элемента. Если он возвращает ошибку, внешний вызов Unmarshal останавливается и возвращает эту ошибку. UnmarshalXML должен использовать ровно один элемент XML. Одна из распространенных стратегий внедрения состоит в том, чтобы демаршалировать в отдельное значение с макетом, соответствующим ожидаемому XML используя d.DecodeElement, а затем копировать данные из этого значения в приемник. Другая распространенная стратегия - использовать d.Token для обработки XML объекта по одному токену за раз. UnmarshalXML не может использовать d.RawToken.

С помощью реализации интерфейса Unmarshaler можно задавать пользовательские способы демаршалинга XML объектов.

Пример реализации интерфейса Unmarshaler

 package main

import (
    "encoding/xml"
    "fmt"
    "log"
    "strings"
)

type Animal int

const (
    Unknown Animal = iota
    Gopher
    Zebra
)

func (a *Animal) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var s string
    if err := d.DecodeElement(&s, &start); err != nil {
        return err
    }
    switch strings.ToLower(s) {
    default:
        *a = Unknown
    case "gopher":
        *a = Gopher
    case "zebra":
        *a = Zebra
    }

    return nil
}

func main() {
    blob := `
    
        gopher
        armadillo
        zebra
        unknown
        gopher
        bee
        gopher
        zebra
    `
    var zoo struct {
        Animals []Animal `xml:"animal"`
    }
    if err := xml.Unmarshal([]byte(blob), &zoo); err != nil {
        log.Fatal(err)
    }

    census := make(map[Animal]int)
    for _, animal := range zoo.Animals {
        census[animal] += 1
    }

    fmt.Printf("Перепись зоопарка:\n* Суслики:     %d\n* Зебры:       %d\n* Неизвестные: %d\n",
        census[Gopher], census[Zebra], census[Unknown])

}

Вывод:

Перепись зоопарка:
* Суслики:     3
* Зебры:       2
* Неизвестные: 3


Читайте также:


пятница, 26 июня 2020 г.

Пакет xml в Golang - метод Unmarshal

func Unmarshal(data []byte, v interface{}) error

Unmarshal анализирует данные в кодировке XML и сохраняет результат в значении, указанном v, которое должно быть произвольной структурой, срезом или строкой. Правильно сформированные данные, которые не вписываются в v, отбрасываются.

Поскольку Unmarshal использует пакет reflect, он может назначать только экспортированные поля (в верхнем регистре). Unmarshal использует сравнение с учетом регистра для сопоставления имен элементов XML с тегами значений и структурных имен полей.

Unmarshal отображает XML-элемент в структуру, используя следующие правила. В правилах тег поля ссылается на значение, связанное с ключом 'xml' в теге поля структуры.

  • Если структура имеет поле типа []byte или string с тегом ",innerxml", Unmarshal накапливает сырой XML, вложенный в элемент в этой области. Остальные правила все еще применяются.
  • Если в структуре есть поле с именем XMLName типа Name, Unmarshal записывает имя элемента в этом поле.
  • Если у поля XMLName есть связанный тег формы "name" или "namespace-URL name", элемент XML должен иметь имя (и, возможно, пространство имен) или Unmarshal возвращает ошибку.
  • Если элемент XML имеет атрибут, имя которого соответствует имени структурного поля со связанным тегом, содержащим ",attr" или явное имя в теге структурного поля вида "name,attr", Unmarshal записывает значение атрибута в это поле.
  • Если элемент XML имеет атрибут, не обработанный предыдущим правилом и структура имеет поле со связанным тегом, содержащим ",any,attr", Unmarshal записывает значение атрибута в первое такое поле.
  • Если элемент XML содержит символьные данные, эти данные накапливается в первом структурном поле с тегом "chardata". Поле структуры может иметь тип []byte или string. Если такого поля нет, символьные данные отбрасываются.
  • Если элемент XML содержит комментарии, они накапливаются в первое поле структуры, которое имеет тег ",comment". Структурное поле может иметь тип []byte или string. Если нет такого поля, комментарии отбрасываются.
  • Если элемент XML содержит подэлемент, имя которого совпадает префикс тега, отформатированный как "a" или "a>b>c", unmarshal опустится в структуру XML в поисках элементов с заданными именами, и сопоставит самые внутренние элементы с этой структурой поле. Тег, начинающийся с ">", эквивалентен одному стартовому с именем поля, сопровождаемым ">".
  • Если элемент XML содержит подэлемент, имя которого совпадает с тегом XMLName поля структуры, а поле структуры не имеет явный тег name в соответствии с предыдущим правилом, демаршал картирует подэлемент в этом поле структуры.
  • Если элемент XML содержит подэлемент, имя которого соответствует поле без каких-либо флагов режима (",attr", ",chardata" и т. д.), Unmarshal сопоставляет подэлемент с этим структурным полем.
  • Если элемент XML содержит подэлемент, который не соответствует ни одному из вышеперечисленных правил и структура имеет поле с тегом ",any", unmarshal отображает вложенный элемент в это структурное поле.
  • Анонимное поле структуры обрабатывается так, как если бы поля его значения были частью внешней структуры.
  • Поле структуры с тэгом "-" никогда не записывается.

Если Unmarshal встречает тип поля, который реализует интерфейс Unmarshaler, Unmarshal вызывает свой метод UnmarshalXML для получения значения из элемента XML. В противном случае, если значение реализует encoding.TextUnmarshaler, Unmarshal вызывает метод UnmarshalText этого значения.

Unmarshal отображает элемент XML в string или []byte, сохраняя объединение символьных данных этого элемента в string или []byte. Сохраненный []byte никогда не равен nil.

Unmarshal отображает значение атрибута в string или []byte, сохраняя значение в строке или срезе.

Unmarshal отображает значение атрибута в Attr, сохраняя атрибут, включая его имя, в Attr.

Unmarshal отображает элемент XML или значение атрибута на срез, увеличивая длину среза и сопоставляя элемент или атрибут с вновь созданным значением.

Unmarshal отображает значение элемента XML или атрибута в логическое значение, устанавливая для него логическое значение, представленное строкой. Пустое пространство обрезается и игнорируется.

Unmarshal отображает элемент XML или значение атрибута в целочисленное поле или поле с плавающей запятой, устанавливая в поле результат интерпретации строкового значения в десятичном виде. Нет проверки на переполнение. Пустое пространство обрезается и игнорируется.

Unmarshal отображает элемент XML на имя путем записи имени элемента.

Unmarshal отображает элемент XML на указатель, устанавливая указатель на только что выделенное значение, а затем сопоставляя элемент с этим значением.

Отсутствующий элемент или пустое значение атрибута будет отменено как нулевое значение. Если поле является срезом, к нему будет добавлено нулевое значение. В противном случае поле будет установлено в его нулевое значение.

Пример использования Unmarshal

Этот пример демонстрирует демаршализацию выдержки XML в значение с некоторыми предустановленными полями. Обратите внимание, что поле Phone не изменено, а элемент XML <Company> игнорируется. Кроме того, поле Groups назначается с учетом пути элемента, указанного в его теге.

package main

import (
    "encoding/xml"
    "fmt"
)

func main() {
    type Email struct {
        Where string `xml:"where,attr"`
        Addr  string
    }
    type Address struct {
        City, State string
    }
    type Result struct {
        XMLName xml.Name `xml:"Person"`
        Name    string   `xml:"FullName"`
        Phone   string
        Email   []Email
        Groups  []string `xml:"Group>Value"`
        Address
    }
    v := Result{Name: "none", Phone: "none"}

    data := `
        <Person>
            <FullName>Grace R. Emlin</FullName>
            <Company>Example Inc.</Company>
            <Email where="home">
                <Addr>gre@example.com</Addr>
            </Email>
            <Email where='work'>
                <Addr>gre@work.com</Addr>
            </Email>
            <Group>
                <Value>Friends</Value>
                <Value>Squash</Value>
            </Group>
            <City>Hanga Roa</City>
            <State>Easter Island</State>
        </Person>
    `
    err := xml.Unmarshal([]byte(data), &v)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    fmt.Printf("XMLName: %#v\n", v.XMLName)
    fmt.Printf("Name: %q\n", v.Name)
    fmt.Printf("Phone: %q\n", v.Phone)
    fmt.Printf("Email: %v\n", v.Email)
    fmt.Printf("Groups: %v\n", v.Groups)
    fmt.Printf("Address: %v\n", v.Address)
}

Вывод:

XMLName: xml.Name{Space:"", Local:"Person"}
Name: "Grace R. Emlin"
Phone: "none"
Email: [{home gre@example.com} {work gre@work.com}]
Groups: [Friends Squash]
Address: {Hanga Roa Easter Island}


Читайте также:


четверг, 25 июня 2020 г.

Пакет xml в Golang - метод Marshal

Пакет xml реализует простой анализатор XML 1.0, который понимает пространства имен XML.

func Marshal(v interface{}) ([]byte, error)

Marshal возвращает XML кодированный v.

Marshal обрабатывает массив или срез путем маршалинга каждого из элементов. Marshal обрабатывает указатель путем маршалинга значения, на которое он указывает, или, если указатель равен nil, ничего не записывая. Marshal обрабатывает значение интерфейса путем маршалирования содержащегося в нем значения или, если значение интерфейса равно nil, ничего не записывая. Marshal обрабатывает все остальные данные, записывая один или несколько элементов XML, содержащих данные.

Имя для элементов XML взято в порядке предпочтения:

  • тег в поле XMLName, если данные представляют собой структуру
  • значение поля XMLName типа Name
  • тег поля структуры, используемого для получения данных
  • имя поля структуры, используемого для получения данных
  • название маршалированного типа

Элемент XML для структуры содержит маршалированные элементы для каждого из экспортируемых полей структуры, за исключением:

  • поле XMLName, описанное выше, опущено.
  • поле с тегом "-" опущено.
  • поле с тегом "name,attr" становится атрибутом с данным name в элементе XML.
  • поле с тегом "attr" становится атрибутом с именем поля в элементе XML.
  • поле с тэгом ",chardata" записывается как символьные данные, не как элемент XML.
  • поле с тегом ",cdata" записывается как символьные данные, обернутые в один или несколько тегов <![CDATA[ ... ]]>, а не как элемент XML.
  • поле с тэгом ",innerxml" записывается дословно, не подчинено к обычной процедуре маршалинга.
  • поле с тегом ",comment" записывается как комментарий XML, не объект для обычной процедуры маршалинга. Он не должен содержать строк "--" внутри него.
  • поле с тегом, включающим опцию "omitempty", опущено если значение поля пусто. Пустые значения false, 0, любой nil указатель или значение интерфейса и любой массив, срез, карта или строка нулевой длины.
  • анонимное структурное поле обрабатывается так, как если бы поля его значение было частью внешней структуры.
  • поле, реализующее Marshaler, пишется путем вызова его MarshalXML метода.
  • поле, реализующее encoding.TextMarshaler записывается путем кодирования результат его метода MarshalText как текст.

Если в поле используется тег"a>b>c", то элемент c будет вложен в родительские элементы a и b. Поля, которые появляются рядом друг с другом и называют одного и того же родителя, будут заключены в один элемент XML.

Если имя XML для поля структуры определяется как тегом поля, так и полем XMLName структуры, имена должны совпадать.

Marshal вернет ошибку, если будет предложено маршалировать канал, функцию или карту.

Метод MarshalIndent

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

MarshalIndent работает как Marshal, но каждый элемент XML начинается с новой строки с отступом, которая начинается с prefix и сопровождается одной или несколькими копиями indent в зависимости от глубины вложения.

Пример использования Marshal (MarshalIndent)

package main

import (
    "encoding/xml"
    "fmt"
    "os"
)

func main() {
    type Address struct {
        City, State string
    }
    type Person struct {
        XMLName   xml.Name `xml:"person"`
        Id        int      `xml:"id,attr"`
        FirstName string   `xml:"name>first"`
        LastName  string   `xml:"name>last"`
        Age       int      `xml:"age"`
        Height    float32  `xml:"height,omitempty"`
        Married   bool
        Address
        Comment string `xml:",comment"`
    }

    v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
    v.Comment = " Need more details. "
    v.Address = Address{"Hanga Roa", "Easter Island"}

    output, err := xml.MarshalIndent(v, "  ", "    ")
    if err != nil {
        fmt.Printf("error: %v\n", err)
    }

    os.Stdout.Write(output)
}

Вывод:

  <person id="13">
      <name>
          <first>John</first>
          <last>Doe</last>
      </name>
      <age>42</age>
      <Married>false</Married>
      <City>Hanga Roa</City>
      <State>Easter Island</State>
      <!-- Need more details. -->
  </person>


Читайте также: