【问题标题】:getting values from xml error: AttributeError: 'NoneType' object has no attribute 'text'从 xml 错误中获取值:AttributeError:“NoneType”对象没有属性“文本”
【发布时间】:2021-07-22 21:24:47
【问题描述】:

我从 api 获取一些 json,并通过 json 响应将数据写入 csv 文件。一些 json 键是数字(实际上是字符串,但可以转换),其中一些是字符串。如果它们是数字,我需要将它们转换为实际名称。我有一个 xml 文件作为排序查找表,我在从 xml 字段获取文本时遇到了一些错误。

.json 文件

[
    {
    "id": "228",
    "fullName2": "users name",
    "4600.0": "0000-00-00",
    "4600.2": "some text",
    }
]

columnLookup.xml 文件作为查找表

<data>
 <fields>
   <field id="NA" alias="fullName2">full name</field>
   <field id="15493" alias="id">id</field>
   <field id="4600.0" alias="jobTitle">Job Title</field>
 </fields>
</data>

.py 文件我的代码

def write_to_csv_file(self):
        with open(self.mJSON_file_name) as json_file:
            data = json.load(json_file)

            csv_file = open(self.mCSV_file_name, 'w')

            csv_writer = csv.writer(csv_file)
            count = 0
            keys_dict = {}
            for emp in data:
                if count == 0:
                    header = emp.keys()
                    for key in header:
                        headers = get_name_from_id(key)
                        keys_dict[headers] = ''
                    
                    csv_writer.writerow(keys_dict.keys())
                    count += 1
                csv_writer.writerow(emp.values())
        
        csv_file.close()
        self.mCSV_data = csv_file

def get_name_from_id( search_id):
    query = 'field[@id="{}"]'.format(search_id)
    alias_query = 'field[@alias="{}"]'.format(search_id)

    tree = ET.parse('columnLookup.xml')
    root = tree.getroot()

    for data in root.iter('fields'):
        if search_id.isdigit():
            return data.find(query).text
        else:
            return data.find(alias_query).text

解释:

基本上我从 json 文件中获取该密钥并将其传递给 get_name_from_id() 函数。如果它可以转换为数字。我在 xml 文件中查找值的 id 并返回文本。如果它不能转换为数字,那么我从字段的别名中查找文本值。我在“fullName2”上崩溃了。

当 json 文件中的键是“fullName2”时,我需要它在 xml 文件中找到 alias="fullName2" 的字段,然后返回文本“fullname”。任何想法为什么我得到属性错误?

【问题讨论】:

  • 试试query = '//field[@id="{}"]'.format(search_id),这是一个相对路径查找。
  • 总是将完整的错误消息(从单词“Traceback”开始)作为文本(不是截图,不是链接到外部门户)有问题(不是评论)。还有其他有用的信息。
  • 错误意味着它不能很好的元素所以你得到None并尝试做None.text。首先你应该得到item = data.find(query)item = data.find(alias_query) 并检查itme 是否不是None 然后使用return item.text
  • 你的 for-loop 是错误的 - 你在 ifelse 中有 return 所以它在第一个元素 fields 上退出函数。
  • @LMC 我认为它还需要在开始时添加点 - './/field[@id="{}"]' - 使其相对于 data

标签: python json xml


【解决方案1】:

我尝试直接在代码中使用数据创建最小的工作代码,但我发现问题使 isdigit() 仅检查字符串中是否有数字 - 如果这是带有 integerfloat 的字符串 - 所以"4600.0".isdigit()给出False 并在alias 而不是id 中搜索它。

您不应该使用isdigit,但同时检查xpath,如果其中一个提供node,则返回`text

for data in root.iter('fields'):
    if data.find(query):
        return data.find(query).text
    if data.find(alias_query):
        return data.find(alias_query).text

其他问题可能是您没有使用相对xpath,因此它在错误的位置搜索。

query = './/field[@id="{}"]'.format(search_id)
alias_query = './/field[@alias="{}"]'.format(search_id)

最少的工作代码

text_json ='''[
    {
    "id": "228",
    "fullName2": "users name",
    "4600.0": "0000-00-00",
    "4600.2": "some text"
    }
]'''

text_xml = '''<data>
 <fields>
   <field id="NA" alias="fullName2">full name</field>
   <field id="15493" alias="id">id</field>
   <field id="4600.0" alias="jobTitle">Job Title</field>
 </fields>
</data>
'''

import json
import csv
import lxml.etree as ET

class Test():
        
    def write_to_csv_file(self):
        #with open(self.mJSON_file_name) as json_file:
        #    data = json.load(json_file)
        data = json.loads(text_json)
        
        #with open(self.mCSV_file_name, 'w') as csv_file:
        with open('output.csv', 'w') as csv_file:
            csv_writer = csv.writer(csv_file)
            
            header_added = False
            
            for row in data:
                if not header_added:
                    headers = []
                    for key in row.keys():
                        name = get_name_from_id(key)
                        headers.append(name)
                    csv_writer.writerow(headers)
                    header_added = True
            
                csv_writer.writerow(row.values())
        
        #self.mCSV_data = csv_file

# ----

# read it only once

root = ET.fromstring(text_xml)
#tree = ET.parse('columnLookup.xml')
#root = tree.getroot()

def get_name_from_id(search_id):
    print('[get_name_from_id] search_id:', search_id)
    
    query_id    = './/field[@id="{}"]'.format(search_id)
    query_alias = './/field[@alias="{}"]'.format(search_id)
    
    for fields in root.iter('fields'):
        items = fields.xpath(query_alias)
        if items:
            print('query:', query_alias)
            print('items:', items)
            print('text:', items[0].text)
            return items[0].text
        items = fields.xpath(query_id)
        if items:
            print('query:', query_alias)
            print('items:', items)
            print('text:', items[0].text)
            return items[0].text
        
# --- main ---

t = Test()
t.write_to_csv_file()

【讨论】:

    【解决方案2】:

    两个属性可以同时查询返回第一个结果。假设没有重复并且存在搜索参数。

    from lxml import etree
    tree = etree.parse('test.xml')
    arr = tree.xpath('//field[@id="{0}" or @alias="{0}"]'.format('15493'))
    print(arr[0].text)
    # result: id
    arr = tree.xpath('//field[@id="{0}" or @alias="{0}"]'.format('fullName2'))
    print(arr[0].text)
    # result: full name
    len(arr)
    # result: 1
    # the list contains 1 element
    

    【讨论】:

      猜你喜欢
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-06
      相关资源
      最近更新 更多