Jaspersoft - Python - LXML - CSV - переменные дочерние теги - PullRequest
0 голосов
/ 27 мая 2018

Я пытаюсь экспортировать список пользователей с сервера Jaspersoft в формат CSV, так как в настоящее время у нас нет доступа к БД или какой-либо панели администрирования, и нам придется собирать заявку в службу поддержки и ждать 2 дня каждый раз, когда мыхотел бы список.Я решил попытать счастья, используя REST API и python, и с помощью модуля запросов мне удалось экспортировать XML, содержащий эту информацию.Формат извлеченного XML таков.

Пример XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<users>
    <user>
        <emailAddress>doejoe@email.com</emailAddress>
        <enabled>true</enabled>
        <fullName>John Doe</fullName>
        <username>doejoe</username>
        <roles>
            <role>
                <externallyDefined>false</externallyDefined>
                <name>MANAGER</name>
                <desc>Beatings will continue until morale improves</desc>
            </role>
            <role>
                <externallyDefined>false</externallyDefined>
                <name>DIRECTOR</name>
            </role>
        </roles>
    </user>
     <user>
        <emailAddress>kathysmith@email.com</emailAddress>
        <enabled>true</enabled>
        <fullName>Kathy Smith</fullName>
        <username>kathysmith</username>
        <externallyDefined>false</externallyDefined>
        <roles>
            <role>
                <externallyDefined>false</externallyDefined>
                <name>USER</name>
                <desc>User Description</desc>
            </role>
            <role>
                <externallyDefined>false</externallyDefined>
                <name>SUPER_MANAGER</name>
                <desc>Super Manager description.</desc>
            </role>
             <role>
                <externallyDefined>false</externallyDefined>
                <name>SUPER_DIRECTOR</name>
            </role>
        </roles>
    </user>
</users>

Мой код до сих пор:

import lxml.etree as ET
import csv

# load file
tree = ET.parse('Format.xml')
# iterate through each user tag
users = tree.findall('.//user')

with open('user_list.csv', "wb") as csv_file:
    writer = csv.writer(csv_file, delimiter=',')
    for user in users:
        email = user.find('emailAddress').text
        enabled = user.find('enabled').text
        externallyDefined = user.find('externallyDefined').text
        fullName = user.find('fullName').text
        tenantId = user.find('tenantId').text
        username = user.find('username').text
        writer.writerow(email + ',' + enabled + ',' + externallyDefined + ',' + fullName + ',' + tenantId + ',' + username)

Как вы можете сказать - я не программист и даже не настолько близок к этому, поэтомуизвиняюсь, если у тебя кровоточат глаза - начал изучать питон 3 недели назад.Мой код не работает по нескольким причинам:

  • Каждый пользователь может иметь от 1 до X ролей, поэтому во всех примерах, которые мне удалось найти, стандартное количество детей.
  • Некоторые пользовательские атрибуты могут отсутствовать, поэтому я получаю сообщение об ошибке, когда не обнаружен.
  • Некоторые атрибуты повторяются, например, ExternallyDefined как для пользователя, так и для роли!
  • Мои циклы экспортируют каждую букву в отдельную ячейку, ноэто то, что я верю, что могу решить.

Моя конечная цель будет выглядеть примерно так:

Excel Image

Любая помощь / направление, как я буду решатьЛюбой из этих вопросов будет очень признателен, так как я полностью потерян на этом этапе.Хороших выходных!

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

Общий подход прост.

  • Итерировать всех пользователей,
  • извлекать значения фиксированного набора выражений XPath для пользователя,
  • записывать эти значения в файл CSV

... который мы можем обобщить как:

  • Итерировать все результаты исходного выражения XPath,
  • извлекать значения фиксированного набора выражений XPath для каждого результата,
  • возвращает их как вложенный список (то есть "строки столбцов")

Для этого нам в основном требуется

  • Одна функция, давайте назовем ее extract_val(), который принимает узел XML, запускает для него XPath и возвращает первое найденное значение.
    Это необходимо, поскольку метод .xpath() lxml может возвращать оба простых значения (строки, логические значения, числа с плавающей запятой)) и списки узлов или значений.
  • Функция, скажем, xml_extract(), которая может взять документ XML, выполнить итерации по объектам и применить наш extract_val() к каждому из них, возвращая список значений
  • Список XPath-ов, которые мы хотим извлечь - это будет соответствовать нашемуСтолбцы CSV позже
  • Наконец csv.writerows(), чтобы записать их все сразу.

В коде:

import lxml.etree as ET

def extract_val(context_node, xpath):
    '''Extracts one value from an XML node'''
    result = context_node.xpath(xpath)
    if isinstance(result, list):
        result = result[0] if len(result) > 0 else None
    if isinstance(result, ET._Element):
        return result.text
    if isinstance(result, (bool, float, str)):
        return result

def xml_extract(tree_or_path, object_xpath, property_xpaths):
    '''Extracts lists of values from an XML tree (or path to an XML file)'''
    if isinstance(tree_or_path, ET._ElementTree):
        tree = tree_or_path
    if isinstance(tree_or_path, str):
        tree = ET.parse(tree_or_path)
    for elem in tree.xpath(object_xpath):
        yield [extract_val(elem, path) for path in property_xpaths]

#----------------------------------------------------------------------
import csv

with open('user_list.csv', 'w', encoding='utf8', newline='') as csv_file:
    writer = csv.writer(csv_file, delimiter=',')
    rows = xml_extract('Format.xml', '//user', [
        'emailAddress',
        'enabled',
        'externallyDefined',
        'fullName',
        'tenantId',
        'username',
        'count(roles/role[name = "DIRECTOR"]) > 0',
        'count(roles/role[name = "MANAGER"]) > 0',
    ])
    writer.writerows(rows)

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

0 голосов
/ 27 мая 2018

попробуйте что-то вроде этого:

import lxml.etree as ET
import csv

# load file
tree = ET.parse('users.xml')
# iterate through each user tag
users = tree.findall('.//user')

# just w mode, no wb. wb is for binary data
with open('user_list.csv', "w") as csv_file:
    writer = csv.writer(csv_file, delimiter=',')
    # write headers
    writer.writerow([
        'email', 'enabled', 'externallyDefined',
        'fullName', 'tenantId', 'username', 'director',
        'manager', 'user', 'super manager', 'super director'
        ])
    for user in users:
        email = user.find('emailAddress').text
        enabled = user.find('enabled').text

        # process optional element
        externallyDefined = user.find('externallyDefined')
        if externallyDefined is not None:
            externallyDefined = externallyDefined.text

        fullName = user.find('fullName').text

        # another optional element
        tenantId = user.find('tenantId')
        if tenantId is not None:
            tenantId = tenantId.text

        username = user.find('username').text

        # collect nested elements (roles)
        user_roles = {}
        roles = user.find('roles').findall('role')
        for role in roles:
            user_roles[role.find('name').text] = True

        writer.writerow([
            email, enabled, externallyDefined, fullName,
            tenantId,  username, user_roles.get('DIRECTOR'),
            user_roles.get('MANAGER'), user_roles.get('USER'),
            user_roles.get('SUPER_MANAGER'), user_roles.get('SUPER_DIRECTOR')
            ])
...