Как разобрать XML в формате слайса - PullRequest
0 голосов
/ 06 января 2020

У меня есть это XML:

   <?xml version="1.0" encoding="ASCII"?><QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd"><Answer><QuestionIdentifier>annotatedResult.boundingBoxes</QuestionIdentifier><FreeText>[{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]</FreeText></Answer><Answer><QuestionIdentifier>annotatedResult.inputImageProperties.height</QuestionIdentifier><FreeText>839</FreeText></Answer><Answer><QuestionIdentifier>annotatedResult.inputImageProperties.width</QuestionIdentifier><FreeText>1260</FreeText></Answer></QuestionFormAnswers>

Я не знаю, как выполнить синтаксический анализ, чтобы получить [{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}] и получить отдельные значения, такие как height, weight и т. Д. c. Наконец получаю 641, 82, 97 и 1088

Я попробовал это, основываясь на найденных руководствах:

type DimensionInfo struct {

    Answer  struct {
    FreeText []string `xml:"freetext"`
    } `xml:"answer"`
}


var data = []byte(`<?xml version="1.0" encoding="ASCII"?><QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd"><Answer><QuestionIdentifier>annotatedResult.boundingBoxes</QuestionIdentifier><FreeText>[{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]</FreeText></Answer><Answer><QuestionIdentifier>annotatedResult.inputImageProperties.height</QuestionIdentifier><FreeText>839</FreeText></Answer><Answer><QuestionIdentifier>annotatedResult.inputImageProperties.width</QuestionIdentifier><FreeText>1260</FreeText></Answer></QuestionFormAnswers>`) 

var t DimensionInfo
xml.Unmarshal(data, &t)
fmt.Println(t.Answer.FreeText)

Я получаю пустой фрагмент / список

1 Ответ

2 голосов
/ 06 января 2020

Проблемы

Есть две проблемы:

  1. Ошибка, возвращаемая xml.Unmarshal, игнорируется, но происходит сбой функции в указанном потоке входных данных, так как вызываемое XML объявление (которое <?xml ... ?> бит) объявляет, что поток данных кодируется с использованием кодировки, отличной от UTF-8 - единственной допустимой кодировки, определенной стандартом¹, - и поэтому декодер просто отказывается продолжать.

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

    Поскольку декодеру было явно сказано, скажем, искать узел с именем "свободный текст", он игнорирует узлы с именем "FreeText" и т. д.

Решения

Имена XML узлов и struct тегов

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

Альтернативное решение заключается в использовании способности декодера понимать «вложенные» имена в тегах полей: вы можете удалить дополнительный вложенный тип данных, используя

type DimensionInfo struct {
    FreeText []string `xml:"Answer>FreeText"`
}

Этот тег указывает декодеру заполнить поле FreeText содержимым любого узла с именем FreeText, который расположен непосредственно под любым узлом с именем Answer.

Кодировка источника data

Проблема с кодированием является более громоздкой - в общем случае.

Поскольку единственное допустимое кодирование для представления XML документов - это UTF-8 (без BOM), декодер ожидает, что его исходные данные должны быть в кодировке UTF-8.

Для обработки потоков данных, закодированных по-разному, декодер предоставляет программисту способ указать «адаптер», который бы прозрачно перекодировал поток входных данных в UTF. -8-кодированный поток данных, который затем будет использовать декодер.

Чтобы сделать это, вы должны создать (и настроить) экземпляр xml.Decoder вместо прямого запуска xml.Unmarshal (что само по себе создает одноразовый Decoder).

В вашем случае, обращение к xml.Unmarshal(data, &t) в использование явно созданного xml.Decoder - это просто:

dec := xml.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&t); err != nil {
    // handle the error
}
// handle the result

Поле интереса Decoder: CharsetReader, которое представляет собой функцию, которая, если указана, вызывается для получения указанного источника io.Reader когда был создан Decoder и набор символов (кодировка) потока исходных данных, и возвращает другой io.Reader, который при чтении из извлекает данные из считывателя источника, перекодирует их из кодировки источника в UTF -8 и передает его Decoder.

В общем случае вы, вероятно, будете использовать несколько пакетов из иерархии golang.org/x/text/encoding, которые способны понимать множество устаревшие не-Unicode и Unicode-кодировки и обеспечивают прозрачное перекодирование из них в UTF-8.

Тем не менее, в вашем простом случае это, вероятно, самый простой способ 1) полагаться на тот факт, что символы ASCII кодируются точно y так же, как их соответствующие кодовые точки Unicode кодируются с использованием UTF-8; 2) предположим, что ваш поток входных данных действительно ASCII (и не содержит странных символов с кодовыми точками в диапазоне [128..255]. В этом случае мы можем избежать использования источника io.Reader без перекодирования, что приводит нас к следующему решению:

var dec = xml.NewDecoder(bytes.NewReader(data))
dec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
    switch strings.ToLower(charset) {
    case "ascii", "utf-8":
        return input, nil
    default:
        return nil, fmt.Errorf("cannot handle XML encoding: %s", charset)
    }
}

Пример решения

package main

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

const data = `<?xml version="1.0" encoding="ASCII"?>
<QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd">
  <Answer>
    <QuestionIdentifier>annotatedResult.boundingBoxes</QuestionIdentifier>
    <FreeText>[{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]</FreeText>
  </Answer>
  <Answer>
    <QuestionIdentifier>annotatedResult.inputImageProperties.height</QuestionIdentifier>
    <FreeText>839</FreeText>
  </Answer>
  <Answer>
    <QuestionIdentifier>annotatedResult.inputImageProperties.width</QuestionIdentifier>
    <FreeText>1260</FreeText>
  </Answer>
</QuestionFormAnswers>`

func main() {
    type DimensionInfo struct {
        FreeText []string `xml:"Answer>FreeText"`
    }

    var dec = xml.NewDecoder(strings.NewReader(data))
    dec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
        switch strings.ToLower(charset) {
        case "ascii", "utf-8":
            return input, nil
        default:
            return nil, fmt.Errorf("cannot handle XML encoding: %s", charset)
        }
    }

    var t DimensionInfo

    err := dec.Decode(&t)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(t.FreeText)
}

¹ Строго говоря, это не так: XML процессоры должен понимать UTF-8 и UTF-16 , но UTF-8 является стандартом де-факто для 'net.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...