Jaspersoft - Python - LXML - CSV - 可变子标签

时间:2018-05-27 09:35:37

标签: python xml csv lxml

我正在尝试将用户列表从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周前开始学习python。我的代码无法正常工作:

  • 每个用户可以拥有1到X个角色,因此我设法找到的所有示例都有标准数量的孩子。
  • 某些用户属性可能会丢失,因此在找不到时会出现错误。
  • 有些属性正在重复,例如在用户和角色中进行外部定义!
  • 我的循环将每个字母导出​​到一个单独的单元格,但这是我认为我可以解决的问题。

我的最终目标是这样的:

Excel Image

我将非常感谢任何帮助/方向,我将如何解决这些问题,因为我现在完全迷失了。周末愉快!

2 个答案:

答案 0 :(得分:0)

尝试这样的事情:

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')
            ])

答案 1 :(得分:0)

一般方法很简单。

  • 迭代所有用户,
  • 提取每个用户的一组固定XPath表达式的值,
  • 将这些值写入CSV文件

...我们可以概括为:

  • 迭代初始XPath表达式的所有结果,
  • 提取每个结果的一组固定XPath表达式的值,
  • 将这些作为嵌套列表(即“行列”)
  • 返回

为此,我们基本上需要

  • 一个函数,我们称之为extract_val(),它接受​​一个XML节点,对它运行XPath,并返回第一个找到的值。
    这是必要的,因为lxml的.xpath()方法can return都是简单值(字符串,布尔值,浮点数)和节点或值列表。
  • 一个函数,比如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中提取值表,您可能需要turn it into a module,以便重复使用。