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

вторник, 18 сентября 2018 г.

Парсинг XML с помощью SimpleTransformation с пропуском тегов.

Итак, допустим, есть такой XML:
И, допустим, нас интересует только значение тега material (которого, кстати, может и не быть). Напишем такую версию simple transformation:

Применение данной трансформации вызовет исключительную ситуацию, так как в XML нам встретиться тег prod и тег foo, которые мы не предусмотрели в трансформации.

Конечно, их можно добавить в трансформацию и никак не обрабатывать их значения. Но таких ненужных нам тегов может быть много и прописывать их все это некомильфо. Кроме того, могут быть ситуации, когда XML описан не четко и подразумевает, что внутри values могут появляться не известные нам на этапе разработки теги.

Как пропускать все что нам не нужно или мы не знаем что это? Для этого нужно в конец tt:group добавить tt:skip следующим образом:
Здесь мы "говорим", что если нам встречается тег material, то мы берем его значение и помещаем в ROOT.MATERIAL, иначе мы пропускаем 1 тег.
Тег tt:group работает как цикл + switch/case - берет на каждом шаге следующий тег в values и сравнивает по условиям tt:d-cond, если условие проходит - выполняется то, что внутри tt:d-cond, и происходит следующая итерация "цикла". Таким образом, по примеру выше, мы найдем на первой итерации тег material, запишем его значение в root.material, найдем на следующей итерации prod и пропустим его, далее найдем на следующей итерации foo и также пропустим его (и аналогично для других тегов если они будут) и выйдем из values. Тег tt:skip должен быть в конце tt:group.

Но с данной реализацией есть одна неприятность. Толи это баг в реализации SimpleTransfomation, толи это by design. Заключается она в следующем:

Если в исходном XML будет только тег material, это также вызовет ошибку трансформации. Отладка данного случая показывает, что обработав тег material, парсер все равно пытается выполнить tt:skip хотя бы один раз. Делает он это для следующего тега, которым будет закрывающий тег </values>. В итоге правило для самого values не найдет своего закрывающего тега и будет выброшено исключение. Но если в XML есть хотя бы один тег, который нам надо пропустить, то tt:skip сработает 1 раз и далее не будет пытаться пропустить </values>.

Обойти это можно, явно добавив еще один фиктивный тег в конец набора тегов в values в исходном XML. Сделать это можно перед вызовом трансформации например так (XML находится в переменной gv_xml):

REPLACE ALL OCCURRENCES OF '</values>' IN gv_xml WITH '<dummyEnd /></values>' IGNORING CASE.'

а в трансформацию явно добавить обработку тега dummyEnd: Не понятно почему, но данный подход срабатывает, и после обработки dummyEnd парсер не пытается выполнить tt:skip.

Если же работать перестанет, то есть более сложный, но более логичный вариант трансформации (в XML также все еще нужно добавлять фиктивный тег):
Здесь мы заводим переменную skip. Далее при начале обработки тега values мы записываем в переменную skip значение 0. В условии tt:cond-var check="skip=0" мы проверяем, что skip равно 0 и, если это так, то пропускаем один тег с помощью tt:skip. Если мы встречаем тег dummyEnd, то записываем в переменную skip значение 1. Таким образом, после последнего тега в values (а мы позаботились чтобы им был dummyEnd), больше не требуется пропускать теги, tt:skip не будет выполнен, и будет обработан закрывающий </values>