วิธีรับวัตถุสตริงแทน Unicode จาก JSON


276

ฉันใช้Python 2เพื่อแยก JSON จากไฟล์ข้อความที่เข้ารหัส ASCII

เมื่อโหลดไฟล์เหล่านี้ด้วยjsonหรือ simplejsonค่าสตริงของฉันทั้งหมดจะถูกส่งไปยังวัตถุ Unicode แทนที่จะเป็นวัตถุสตริง ปัญหาคือฉันต้องใช้ข้อมูลกับบางไลบรารีที่ยอมรับเฉพาะวัตถุสตริงเท่านั้น ฉันไม่สามารถเปลี่ยนห้องสมุดหรืออัพเดทได้

เป็นไปได้ที่จะรับวัตถุสตริงแทน Unicode หรือไม่

ตัวอย่าง

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

ปรับปรุง

คำถามนี้ถูกถามเป็นเวลานานแล้วเมื่อผมติดอยู่กับงูหลาม 2 ทางออกที่ง่ายและสะอาดสำหรับวันนี้คือการใช้ Python เวอร์ชันล่าสุดเช่นPython 3และส่งต่อ


1
ไม่มีปัญหาภายใต้ Python3 ประเภทของรายการใน new_list คือstr
GoingMyWay

1
Python 3k ไม่ใช่ 'Python เวอร์ชันล่าสุด' มันเป็นเพียงสาขาอื่น
user2589273

11
เป็นเรื่องแปลกที่จะเห็นความคิดเห็นดังกล่าวในเดือนธันวาคม 2017 - Python 2 เลิกใช้แล้วและจะไม่มีการบำรุงรักษาหลังจากวันที่ 1 มกราคม 2020 ซึ่งน้อยกว่า 2 ปี: pythonclock.org
Zaar Hai

1
@ZaarHai ผู้คนจำนวนมากติดอยู่ใน Python 2 กับความประสงค์ของพวกเขา มีหลายโปรแกรมที่ฝังรุ่นหลามของตัวเองสำหรับระบบอัตโนมัติและการเขียนสคริปต์เพื่อให้ผู้คนต้องใช้มันจนกว่าการปรับปรุงผู้ขาย (ฉันกำลังมองหาที่คุณมายาฮูดินี่ Nuke .. ) มี
Geordie

1
@Gordie ฉันรู้และเข้าใจอย่างแน่นอน ความคิดเห็นของฉันเกี่ยวกับคำศัพท์ - Python ไม่ใช่ "ทางเลือกสาขา" แต่ค่อนข้างโชคร้ายที่ไม่มีทางเลือก (ปุนตั้งใจ) สำหรับผู้ที่ติดอยู่กับมัน
Zaar Hai

คำตอบ:


101

วิธีแก้ปัญหาด้วย object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

ตัวอย่างการใช้งาน:

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

มันทำงานอย่างไรและทำไมฉันถึงใช้มัน?

ฟังก์ชัน Mark Ameryนั้นสั้นกว่าและชัดเจนกว่าเหล่านี้ดังนั้นประเด็นของพวกเขาคืออะไร? ทำไมคุณต้องการที่จะใช้พวกเขา?

เพื่อประสิทธิภาพอย่างแท้จริง คำตอบของ Mark จะถอดรหัสข้อความ JSON อย่างสมบูรณ์ก่อนด้วยสตริง Unicode จากนั้นเรียกใช้ซ้ำทั้งค่าที่ถอดรหัสทั้งหมดเพื่อแปลงสตริงทั้งหมดเป็นสตริงไบต์ สิ่งนี้มีเอฟเฟกต์ที่ไม่พึงประสงค์สองประการ:

  • สำเนาของโครงสร้างถอดรหัสทั้งหมดจะถูกสร้างขึ้นในหน่วยความจำ
  • ถ้าวัตถุ JSON ของคุณเป็นจริงที่ซ้อนกันอย่างลึกซึ้ง (500 ระดับหรือมากกว่า) แล้วคุณจะตีลึก recursion สูงสุดของงูใหญ่

คำตอบนี้ช่วยลดผลกระทบทั้งปัญหาการปฏิบัติงานเหล่านั้นโดยใช้object_hookพารามิเตอร์ของและjson.load json.loadsจากเอกสาร :

object_hookเป็นฟังก์ชั่นเสริมที่จะถูกเรียกพร้อมกับผลลัพธ์ของการถอดรหัสตามตัวอักษรของวัตถุใด ๆ (a dict) ค่าตอบแทนของ object_hook dictจะถูกนำมาใช้แทน คุณสมบัตินี้สามารถใช้ในการสร้างตัวถอดรหัสที่กำหนดเองได้

เนื่องจากพจนานุกรมซ้อนกันหลายระดับลึกลงไปในพจนานุกรมอื่น ๆ ที่ส่งผ่านไปยังobject_hook พวกเขากำลังถอดรหัสเราสามารถไบต์สตริงหรือรายการภายในพวกเขา ณ จุดนั้นและหลีกเลี่ยงความจำเป็นในการเรียกซ้ำลึก ๆ ในภายหลัง

คำตอบของมาร์กไม่เหมาะสำหรับใช้ในฐานะที่object_hookเป็นอยู่เพราะเป็นคำที่ซ้ำซ้อนกันในพจนานุกรม เราป้องกันการเรียกซ้ำในคำตอบนี้ด้วยignore_dictsพารามิเตอร์ถึง_byteifyซึ่งจะถูกส่งไปยังมันตลอดเวลายกเว้นเมื่อobject_hookผ่านมันdictไปใหม่เพื่อ byteify ignore_dictsธงบอกว่า_byteifyจะไม่สนใจdictตั้งแต่พวกเขาได้รับอยู่แล้ว byteified

ในที่สุดการใช้งานjson_load_byteifiedและการjson_loads_byteifiedโทร_byteify(พร้อมignore_dicts=True) กับผลลัพธ์ที่ส่งคืนจากjson.loadหรือjson.loadsเพื่อจัดการกับกรณีที่ข้อความ JSON ที่กำลังถอดรหัสไม่มีdictระดับสูงสุด


1
+1 สำหรับวิธีการที่นี่; ฉันไม่เข้าใจจริง ๆ เมื่อฉันอ่านครั้งแรก แต่ในที่สุดก็เข้าใจเมื่ออ่านซ้ำในคำตอบของเทรวิสเซ่น ฉันได้ทำการแก้ไขที่ค่อนข้างก้าวร้าวโดยหวังว่าจะอธิบายให้ชัดเจนว่ามันทำงานอย่างไรและข้อดีของคำตอบคืออะไร แนวคิดหลักของรหัสยังคงไม่มีการเปลี่ยนแปลง แต่ฉันได้แก้ไขทุกอย่างแล้ว อย่าลังเลที่จะย้อนกลับการแก้ไขของฉันหากคุณคัดค้านสิ่งนี้ - เป็นคำตอบของคุณ!
Mark Amery

ไม่มีปัญหามาร์คขอบคุณมาก ฉันชอบการแก้ไขของคุณมันเป็นคำอธิบายที่มากกว่าต้นฉบับของฉัน บางทีวันหนึ่งฉันจะเรียนรู้ที่จะให้คำตอบที่กระชับมากขึ้น
Mirec Miskuf

2
นี่คือทางออกที่ดี มีประสิทธิภาพและสง่างาม แต่ถ้าคุณกำลังติดอยู่ในดินแดนของงูใหญ่ <2.7 เป็นฉันคุณจะต้องเปลี่ยนบรรทัด: return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }มีreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())ให้มันทำงาน
Richard Dunn

ฉันคิดว่าคุณผิดเกี่ยวกับปัญหาความลึกในการเรียกซ้ำ กับคุณฉันสามารถไปได้ถึง json_loads_byteified('[' * 990 + ']' * 990)990: ด้วย 991 มันล้มเหลว มาร์คยังคงทำงานร่วมกับ byteify(json.loads('[' * 991 + ']' * 991))991: มันล้มเหลวที่ 992 ดังนั้นอย่างน้อยในการทดสอบนี้มาร์คสามารถลงลึกกว่าที่คุณพูด
Stefan Pochmann

@ MarkAmery คุณคิดอย่างไรเกี่ยวกับความคิดเห็นข้างต้นของฉัน (ฉันเพิ่งเห็นในประวัติการแก้ไขว่าจริงๆแล้วคุณเป็นผู้เพิ่มการอ้างสิทธิ์นั้น)
Stefan Pochmann

180

ในขณะที่มีบางคำตอบที่ดีที่นี่ฉันลงเอยด้วยการใช้PyYAMLเพื่อแจงไฟล์ JSON ของฉันเนื่องจากมันให้คีย์และค่าเป็นstrสตริงประเภทแทนที่จะเป็นunicodeประเภท เนื่องจาก JSON เป็นชุดย่อยของ YAML จึงทำงานได้ดี:

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

หมายเหตุ

บางสิ่งที่ควรทราบ:

  • ฉันจะได้รับวัตถุสตริงเพราะทุกรายการของฉันASCII เข้ารหัส ถ้าฉันจะใช้รายการที่เข้ารหัสแบบ Unicode ฉันจะเอามันกลับมาเป็นวัตถุแบบ Unicode - ไม่มีการแปลง!

  • คุณควร (อาจจะเสมอ) ใช้safe_loadฟังก์ชันของ PyYAML หากคุณใช้เพื่อโหลดไฟล์ JSON คุณไม่จำเป็นต้องมี "กำลังเพิ่มเติม" ของloadฟังก์ชั่นอยู่ดี

  • หากคุณต้องการตัวแยกวิเคราะห์ YAML ที่สนับสนุนสเป็ครุ่น 1.2 (และแยกวิเคราะห์ตัวเลขที่ต่ำมากอย่างถูกต้อง ) ลองRuamel YAML : pip install ruamel.yamlและimport ruamel.yaml as yamlทั้งหมดที่ฉันต้องการในการทดสอบของฉันคือ

การแปลง

ตามที่ระบุไว้ไม่มีการแปลง! หากคุณไม่สามารถแน่ใจได้ว่าจะจัดการกับค่า ASCII เท่านั้น (และคุณไม่สามารถมั่นใจได้ว่าส่วนใหญ่) ให้ใช้ฟังก์ชันการแปลงที่ดีกว่า:

ฉันใช้ตัวนี้จากMark Ameryสองสามครั้งตอนนี้มันใช้งานได้ดีและใช้งานง่ายมาก นอกจากนี้คุณยังสามารถใช้ฟังก์ชั่นที่คล้ายกันobject_hookแทนเพราะมันอาจเพิ่มประสิทธิภาพให้กับไฟล์ขนาดใหญ่ ดูคำตอบที่เกี่ยวข้องเพิ่มเติมจาก Mirec Miskuf เล็กน้อยสำหรับสิ่งนั้น


8
ใช้ความระมัดระวังเล็กน้อยถ้าคุณตัดสินใจใช้คำตอบนี้ มันทำงานได้อย่างสมบูรณ์แบบสำหรับกรณีของ Brutus แต่เพียงเพราะเขารู้ว่าข้อมูลของเขามีอักขระที่เข้ารหัสได้ ASCII เท่านั้น หากคุณไม่มีการรับประกันนั้นคำตอบนี้จะไม่ทำงาน ตัวอย่างเช่นลองดำเนินการyaml.load(json.dumps([u'a', u'£', u'É']))ที่ Python shell และสังเกตว่าคุณได้รับกลับมา['a', u'\xa3', u'\xc9'](ซึ่งมีunicodeสตริง) หากคุณไม่แน่ใจว่าข้อมูลของคุณมีอักขระจากชุดอักขระ ASCII เท่านั้นคุณควรใช้วิธีอื่นแทน (ฉันแนะนำคำตอบของฉันเอง)
Mark Amery

1
YAML ยังใช้ความ[u'a', u'b']ระมัดระวัง
Carlos Calla

1
นี่เป็นสิ่งที่ดี แต่มันใช้งานไม่ได้กับตัวเลขที่ต่ำ .. ดูที่นี่: stackoverflow.com/questions/30458977//
Oren

@Oren: นี่ไม่ใช่ข้อผิดพลาดในข้อมูลจำเพาะของ YAMLแต่ในตัวแยกวิเคราะห์ PyYAML โปรแกรมแยกวิเคราะห์ YAML จาก ruamelทำงาน
Brutus

ฉันต้องการให้ ouput เช่น ["a", "b"] ไม่เหมือนกับ ['a', 'b'] @Brutus
user60679

141

ไม่มีตัวเลือกในตัวที่จะทำให้ฟังก์ชั่นโมดูล json ส่งคืนสตริงไบต์แทนสตริง Unicode อย่างไรก็ตามฟังก์ชันเรียกซ้ำที่สั้นและง่ายนี้จะแปลงอ็อบเจ็กต์ JSON ที่ถอดรหัสแล้วใด ๆ จากการใช้สตริง Unicode เป็นสตริงไบต์แบบเข้ารหัส UTF-8:

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

เพียงแค่เรียกสิ่งนี้ในผลลัพธ์ที่คุณได้รับจากการโทรjson.loadหรือjson.loads

หมายเหตุสองประการ:

  • เพื่อรองรับ Python 2.6 หรือเก่ากว่าให้แทนที่return {byteify(key): byteify(value) for key, value in input.iteritems()}ด้วยreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])เนื่องจากความเข้าใจในพจนานุกรมไม่รองรับจนถึง Python 2.7
  • เนื่องจากคำตอบนี้เกิดขึ้นอีกครั้งผ่านวัตถุที่ถอดรหัสทั้งหมดมันมีลักษณะการทำงานที่ไม่พึงประสงค์สองประการที่สามารถหลีกเลี่ยงได้ด้วยการใช้object_hookหรือobject_pairs_hookพารามิเตอร์อย่างระมัดระวัง คำตอบของ Mirec Miskuf เป็นเพียงคำตอบเดียวที่สามารถดึงสิ่งนี้ออกได้อย่างถูกต้องแม้ว่าจะเป็นผลทำให้มันซับซ้อนกว่าแนวทางของฉันมาก

1
ฉันชอบสิ่งนี้ - มันไม่ได้เพิกเฉย - มันเป็นการรับรู้ว่าเมื่อผู้คนพูดว่า "สาย" และ "ascii" พวกเขาส่วนใหญ่ไร้เดียงสาหมายความว่าพวกเขาต้องการไบต์ไม่ใช่ตัวละครยูนิโค้ดในทางทฤษฎี (และไม่ใช่ ASCII เนื่องจากพวกเขายังต้องการสัญญาณปอนด์ที่ปลายอีกด้าน)
Danny Staple

ฉันชอบสิ่งนี้มันทำงานได้เกือบเหมือนกับเครื่องพิมพ์สวย ๆ ของฉันเพราะฉันรู้ว่า json ไม่ทำให้สิ่งผิดคุณควรเพิ่มข้อยกเว้นสำหรับ tuple ด้วย
y.petremann

นี่เป็นสิ่งที่ไม่มีประสิทธิภาพอย่างน่ากลัวโดยคุณจะต้องทำการสำรวจโหนดซ้ำ ๆ ที่คุณอาจไม่จำเป็นต้องทำ โมดูล json ให้คุณเชื่อมต่อได้อย่างมีประสิทธิภาพมากขึ้น คำตอบที่ใช้ด้านล่างobject_hookนั้นเลวร้ายยิ่งกว่าที่เป็นอยู่นี้ แต่การใช้object_pairs_hookคุณสามารถสร้างวิธีที่มีประสิทธิภาพพอสมควรโดยไม่ต้องเรียกใช้ซ้ำหรือเรียกคืนโหนดที่ไม่มีสตริง
Travis Jensen

1
@ TravisJensen ที่น่าสนใจ object_pairs_hookวิธีการอาจจะเล็กน้อยยากที่จะเข้าใจกว่านี้ (คุณต้องเข้าใจวิธีการทำงานของพารามิเตอร์และทำไมรายการและ dicts ต้องมีการจัดการที่แตกต่างกัน) และผลประโยชน์จะไม่เป็นเรื่องกับคนส่วนใหญ่ ... แต่ฉันคาดหวัง โดยเฉพาะอย่างยิ่งสำหรับทุกคนที่จัดการกับวัตถุ JSON ที่ซ้อนกันอย่างผิดปกติ
Mark Amery

plus1 นี่คือคำตอบที่กระชับที่สุด; นอกจาก PyYAML เป็นความเจ็บปวดในการติดตั้ง สิ่งเดียวที่ดีกว่าคือการแปลงไมโคร - สตรีมไม่ใช้หน่วยความจำ 4X
personal_cloud

74

คุณสามารถใช้object_hookพารามิเตอร์สำหรับjson.loadsส่งต่อในตัวแปลง คุณไม่ต้องทำการแปลงหลังจากข้อเท็จจริง jsonโมดูลเสมอจะผ่านการobject_hookdicts เท่านั้นและมันซ้ำจะผ่านใน dicts ซ้อนกันเพื่อให้คุณไม่ต้อง recurse เข้า dicts ซ้อนกันตัวเอง ฉันไม่คิดว่าฉันจะแปลงสตริงยูนิโค้ดเป็นตัวเลขอย่างที่ Wells แสดงให้เห็น หากเป็นสตริงยูนิโคดจะถูกยกมาเป็นสตริงในไฟล์ JSON ดังนั้นจึงควรเป็นสตริง (หรือไฟล์ไม่ดี)

นอกจากนี้ฉันพยายามหลีกเลี่ยงการทำบางสิ่งบางอย่างstr(val)บนunicodeวัตถุ คุณควรใช้value.encode(encoding)กับการเข้ารหัสที่ถูกต้องขึ้นอยู่กับ lib ภายนอกของคุณ

ตัวอย่างเช่น:

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)

3
สิ่งนี้จะดีถ้าวัตถุในsคือ JSON Object(คอลเลกชันที่ไม่มีการเรียงลำดับของคีย์: คู่ค่าที่มีอักขระ ':' คั่นคีย์และค่าคั่นด้วยเครื่องหมายจุลภาคและล้อมรอบด้วยเครื่องหมายปีกกา) แต่ไม่ใช่ถ้ามันพูด a ArrayJSON ดังนั้นหากได้รับ JSON Arrayเช่นผลที่ได้จะยังคง["a", "b"] [u'a', u'b']ไม่มีพารามิเตอร์การกำหนดชนิด hook แบบอื่นที่json.loads()สามารถใช้งานได้ในขณะนี้
martineau

2
ตั้งแต่ที่คุณกล่าวถึงjsonโมดูลจะผ่านซ้ำในซ้อนกันdictมันไม่จำเป็นที่จะตรวจสอบพวกเขาในสองฟังก์ชั่น - ดังนั้นทั้งสองelifข้อที่ตรวจสอบพวกเขาควรจะถูกลบออก
martineau

1
โปรดทราบว่าการเริ่มต้นชื่อฟังก์ชั่นที่มีเครื่องหมายขีดล่างมีความหมายพิเศษสำหรับคำสั่งนำเข้า หากคุณใส่ฟังก์ชั่นเหล่านี้ในไฟล์ชื่อ Utility.py และในอีกไฟล์หนึ่งทำfrom Utility import *หน้าที่จะไม่สามารถมองเห็นได้เนื่องจากการขีดล่างนั้น
M Katz

1
นี่เป็นความคิดที่แย่จริงๆ object_hookถูกเรียกสำหรับทุก ๆ อ็อบเจ็กต์ json ที่ถูกแจงดังนั้นถ้าคุณรับเงินคืนในสิ่งที่มอบให้คุณคุณกำลัง re- "byteifying" สิ่งที่คุณมีอยู่ "byteified" ประสิทธิภาพจะเพิ่มขึ้นตามขนาดของวัตถุ ฉันได้รวมคำตอบไว้ที่นี่ซึ่งใช้object_pairs_hookแล้วและไม่ประสบปัญหานั้น
Travis Jensen

38

นั่นเป็นเพราะ json ไม่มีความแตกต่างระหว่างวัตถุสตริงและวัตถุ Unicode มันคือสตริงทั้งหมดใน javascript

ผมคิดว่าJSON เป็นสิทธิที่จะกลับวัตถุ ในความเป็นจริงฉันจะไม่ยอมรับอะไรเลยเพราะสตริง javascript ในความเป็นจริงunicodeวัตถุ (เช่นสตริง JSON (จาวาสคริปต์) สามารถจัดเก็บอักขระ unicode ใด ๆ ) ดังนั้นจึงเหมาะสมที่จะสร้างunicodeวัตถุเมื่อแปลสตริงจาก JSON สตริงธรรมดาจะไม่พอดีเนื่องจากไลบรารีจะต้องเดาการเข้ารหัสที่คุณต้องการ

ควรใช้unicodeวัตถุสตริงทุกที่ดีกว่า ดังนั้นตัวเลือกที่ดีที่สุดของคุณคืออัปเดตไลบรารีของคุณเพื่อให้สามารถจัดการกับวัตถุ Unicode ได้

แต่ถ้าคุณต้องการทดสอบจริง ๆ เพียงแค่เข้ารหัสผลลัพธ์ที่ได้จากการเข้ารหัสที่คุณเลือก:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']

ขอบคุณ nosklo นั่นคือสิ่งที่ฉันได้ทำมาก่อน แต่อย่างที่ฉันพูดข้อมูลจริงที่ฉันใช้นั้นค่อนข้างซ้อนกันและทั้งหมดดังนั้นสิ่งนี้จึงแนะนำค่าใช้จ่ายบางอย่าง ฉันยังคงมองหาโซลูชันอัตโนมัติ ... มีรายงานบั๊กอย่างน้อยหนึ่งรายการที่ผู้คนบ่นเกี่ยวกับ simplejson ที่ส่งคืนออบเจ็กต์สตริงแทนที่จะเป็น unicode
Brutus

1
@ Brutus: ฉันคิดว่า json มีสิทธิที่จะคืนค่าวัตถุ Unicode ในความเป็นจริงฉันจะไม่ยอมรับอะไรเลยเพราะสตริง javascript เป็นวัตถุแบบ Unicode สิ่งที่ฉันหมายถึงคือสตริง json (javascript) สามารถเก็บอักขระยูนิโค้ดได้ทุกประเภทดังนั้นจึงเหมาะสมที่จะสร้างออบเจ็กต์ Unicode เมื่อแปลจาก json คุณควรแก้ไขไลบรารีของคุณแทน
nosklo

16

มีการแก้ไขที่ง่าย

TL; DR - ใช้แทนast.literal_eval() json.loads()ทั้งสองastและjsonอยู่ในไลบรารีมาตรฐาน

แม้ว่าจะไม่ใช่คำตอบที่ 'สมบูรณ์แบบ' แต่ก็ทำให้คุณรู้สึกดีถ้าแผนการของคุณไม่สนใจ Unicode ทั้งหมด ใน Python 2.7

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

ให้:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

สิ่งนี้จะมีขนดกมากขึ้นเมื่อวัตถุบางอย่างเป็นสายอักขระ Unicode จริง ๆ คำตอบแบบเต็มได้รับขนอย่างรวดเร็ว


11
ดีกว่าจะแน่ใจ JSON ของคุณไม่ได้มีการใด ๆnull, trueหรือfalseค่าเพราะพวกเขาจะไม่ถูกต้องในหลามและจะทำให้เกิดความliteral_eval()ล้มเหลว
ʇsәɹoɈ

3
@ ʇsәɹoɈหวังว่า JSON ของคุณจะไม่ประกอบด้วยโซลิดหนี ( \/) ภายในสตริงหรือลำดับการยกเว้นยูนิโค้ด (เช่น"\u0061"ซึ่งเป็นอีกวิธีหนึ่งในการเขียน"a") ไวยากรณ์ที่แท้จริงของ Python ไม่สอดคล้องกับ JSON ในหลายวิธีและฉันจะไม่เชื่อคำตอบนี้สำหรับสคริปต์ใด ๆ ที่ฉันจะไม่ทิ้ง
Mark Amery

ผู้คนพูดถูกว่าถ้าสตริงเป็นยูนิโค้ดจริง ๆ แล้วคำตอบนี้ล้มเหลว แต่ถ้าเป็นอย่างนั้นเราคงไม่สามารถส่งสตริงไปได้ +1 สำหรับคำตอบที่ใช้งานได้เมื่อทำงานและส่งข้อยกเว้นเป็นอย่างอื่นเท่านั้น
Stefan Sullivan

ถ้าเป็นไปได้อย่าใช้jsonเพื่อถ่ายโอนข้อมูลเพียงแค่ใช้printถ้าใช้ python จากนั้นast.literal_evalทำงานได้
Jean-François Fabre

11

คำตอบของ Mike Brennanนั้นใกล้เคียงกัน แต่ไม่มีเหตุผลใดที่จะสำรวจโครงสร้างทั้งหมดอีกครั้ง หากคุณใช้object_hook_pairsพารามิเตอร์ (Python 2.7+):

object_pairs_hookเป็นฟังก์ชั่นเสริมที่จะถูกเรียกพร้อมกับผลลัพธ์ของวัตถุใด ๆ ที่ถอดรหัสด้วยรายการคู่ที่สั่ง ค่าตอบแทนของจะถูกนำมาใช้แทนobject_pairs_hook dictคุณลักษณะนี้สามารถใช้ในการใช้ตัวถอดรหัสที่กำหนดเองซึ่งขึ้นอยู่กับลำดับที่คู่คีย์และค่าถูกถอดรหัส (ตัวอย่างเช่นcollections.OrderedDictจะจดจำลำดับของการแทรก) หากobject_hookมีการกำหนดเช่นobject_pairs_hookกัน

ด้วยสิ่งนี้คุณจะได้รับวัตถุ JSON แต่ละตัวเพื่อให้คุณสามารถถอดรหัสได้โดยไม่จำเป็นต้องเรียกซ้ำ

def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'                                        

In [53]: json.load(open('test.json'))
Out[53]: 
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

object_pairs_hookขอให้สังเกตว่าฉันไม่เคยมีการโทรเบ็ดซ้ำตั้งแต่วัตถุทุกคนจะได้รับการส่งให้เบ็ดเมื่อคุณใช้ คุณต้องใส่ใจกับรายการต่างๆ แต่อย่างที่คุณเห็นวัตถุในรายการจะถูกแปลงอย่างเหมาะสมและคุณไม่จำเป็นต้องรับเงินคืนเพื่อให้เกิดขึ้น

แก้ไข: ผู้ร่วมงานชี้ให้เห็นว่า Python2.6 object_hook_pairsไม่ได้ คุณยังสามารถใช้ Python2.6 นี้ได้โดยทำการเปลี่ยนแปลงเล็กน้อย ในตะขอด้านบนเปลี่ยน:

for key, value in pairs:

ถึง

for key, value in pairs.iteritems():

จากนั้นใช้object_hookแทนobject_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

การใช้object_pairs_hookผลลัพธ์ในพจนานุกรมน้อยหนึ่งรายการที่ถูกสร้างอินสแตนซ์สำหรับแต่ละวัตถุในวัตถุ JSON ซึ่งหากคุณแยกวิเคราะห์เอกสารขนาดใหญ่อาจคุ้มค่าในขณะที่


1
นี่เป็นระเบียบและดูเหมือนว่าใกล้เคียงกับเครื่องหมายสีเขียวที่สมควรได้รับ (ซึ่ง Brutus มีน่าชื่นชมและผ่านไปแล้วอย่างเสรีตามคำตอบที่ดีกว่าเข้ามา) แต่ ... ทำไมไม่จัดการรายการจริง ๆ อย่างถูกต้องในสิ่งdeunicodify_hookที่คุณแสดงในคำตอบนี้? ในขณะนี้คุณมีการนำไปใช้งานdeunicodify_hookที่ไม่ซ้ำไปซ้ำมาในรายการและยกเลิกการแปลงค่าสตริงและรายการภายในพวกเขาและผลลัพธ์ที่คุณแสดงไม่ตรงกับผลลัพธ์ที่ตะขอของคุณจะสร้างขึ้นจริง แก้ไขที่และคำตอบนี้จะดีกว่าของฉัน
Mark Amery

เล็ก ๆ น้อย ๆ : ฉันขอแนะนำให้สาธิตฟังก์ชั่นด้วยล่าม CPython ธรรมดามากกว่าที่คุณใช้ที่นี่ (ซึ่งฉันคิดว่าเป็น IronPython) CPython interpreter เป็นที่คุ้นเคยกับผู้ใช้ Python ส่วนใหญ่และในความคิดของฉันดีกว่า
Mark Amery

สิ่งนี้ไม่ได้ผลสำหรับฉัน แต่ฉันแน่ใจว่ามันเป็นสิ่งที่แปลกตาในสิ่งที่ฉันกำลังทำ ... ไม่ว่าฉันจะโหลดโดยมีหรือไม่มี object_pairs_hook นี้ทุกรายการจะเกิดขึ้นเป็น Unicode ยี้
rsaw

1
@rsaw จุดดี! เนื่องจากobject_pairs_hookถูกเรียกใช้เพียงอ็อบเจ็กต์เท่านั้นหากข้อความ JSON ของคุณมีรายการสตริงที่ระดับบนสุดโซลูชันนี้จะล้มเหลว ไม่มีวิธีแก้ไขโดยไม่เรียกใช้ฟังก์ชันบางอย่างกับสิ่งที่ส่งคืนจากjson.load; ไม่มีjson.loadตะขอใดที่รับประกันว่าคุณจะสามารถจัดการกับทุก ๆ สตริงได้ ฉันคิดว่านี่เป็นข้อบกพร่องที่ใหญ่พอสำหรับฉันที่จะแนะนำทางออกให้ฉันโดยใช้ตะขอ
Mark Amery

-1 เพราะฉันเพิ่งรู้ว่า Mirec Miskuf ได้โพสต์คำตอบแบบ object-hook แล้วว่าไม่มีข้อเสียของวิธีการของ Mike Brennan (พจนานุกรมซ้ำซ้ำหลายครั้งหลายครั้ง) หรืออันนี้ (ไม่สามารถเพิ่มรายการซ้อนหรือรายการระดับบนสุดได้ หรือสตริง) ฉันไม่แน่ใจว่าทำไมคำตอบของเขาจึงแทบไม่สนใจเลยในขณะที่คำตอบนี้ซึ่งด้อยกว่า - ได้คะแนนอย่างรวดเร็ว
Mark Amery

9

ฉันเกรงว่าจะไม่มีทางบรรลุผลนี้โดยอัตโนมัติภายในห้องสมุด simplejson

สแกนเนอร์และถอดรหัสใน simplejson ได้รับการออกแบบมาเพื่อผลิตข้อความ Unicode ในการทำเช่นนี้ไลบรารีจะใช้ฟังก์ชันที่เรียกว่าc_scanstring(หากมีให้สำหรับความเร็ว) หรือpy_scanstringรุ่น C ไม่พร้อมใช้งาน scanstringฟังก์ชั่นที่เรียกว่าหลายครั้งโดยเกือบทุกประจำที่มี simplejson สำหรับการถอดรหัสโครงสร้างที่อาจมีข้อความ คุณจะต้องปรับscanstringค่าให้เป็นลิงใน simplejson.decoder หรือซับคลาสJSONDecoderและจัดเตรียมสิ่งที่อาจมีข้อความไว้ในตัวของคุณเอง

เหตุผลที่ simplejson แสดงผล unicode คือjson specระบุว่า "สตริงคือชุดของอักขระ Unicode ที่เป็นศูนย์หรือมากกว่า" ... สนับสนุน unicode จะถือว่าเป็นส่วนหนึ่งของรูปแบบนั้น Simplejson ของscanstringการใช้งานดำเนินไปจนถึงการสแกนและตีความ unicode escapes (แม้แต่การตรวจสอบข้อผิดพลาดสำหรับการแทนค่าหลายชุดอักขระ charset ที่มีรูปแบบไม่ถูกต้อง) ดังนั้นวิธีเดียวที่เชื่อถือได้คืนค่าให้กับคุณคือ unicode

หากคุณมีไลบรารี่เก่าที่ต้องการstrฉันขอแนะนำให้คุณค้นหาโครงสร้างข้อมูลที่ซ้อนกันหลังจากการแยกวิเคราะห์ (ซึ่งฉันยอมรับว่าเป็นสิ่งที่คุณพูดอย่างชัดเจนว่าคุณต้องการหลีกเลี่ยง ... ขอโทษ) หรือห่อห้องสมุดของคุณในบางประเภท ซุ้มที่คุณสามารถนวดพารามิเตอร์การป้อนข้อมูลในระดับที่ละเอียดยิ่งขึ้น วิธีที่สองอาจจัดการได้ดีกว่าวิธีแรกถ้าโครงสร้างข้อมูลของคุณซ้อนกันอย่างแน่นอน


4

ในฐานะที่เป็น Mark (Amery) บันทึกอย่างถูกต้อง: การใช้deserializer ของPyYamlในการถ่ายโอนข้อมูล json จะทำงานได้ก็ต่อเมื่อคุณมี ASCII เท่านั้น อย่างน้อยก็ออกนอกกรอบ

สองความคิดเห็นอย่างรวดเร็วเกี่ยวกับวิธีการ PyYaml:

  1. ไม่เคยใช้ yaml.load กับข้อมูลจากสนาม มันเป็นคุณสมบัติ (!) ของ yaml เพื่อรันโค้ดโดยพลการที่ซ่อนอยู่ภายในโครงสร้าง

  2. คุณสามารถทำให้มันทำงานได้สำหรับ ASCII ที่ไม่ใช่ผ่านทางนี้:

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)

แต่ประสิทธิภาพก็ไม่ควรเปรียบเทียบกับคำตอบของ Mark Amery:

การโยนตัวอย่างที่ซ้อนกันอย่างล้ำลึกวางลงบนสองวิธีฉันได้รับสิ่งนี้ (ด้วย dt [j] = time delta ของ json.loads (json.dumps (m))):

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

deserialization รวมถึงการเดินบนต้นไม้และการเข้ารหัสอย่างสมบูรณ์ตามลำดับความสำคัญของการดำเนินการตาม json ฉันพบว่ามันเร็วอย่างน่าทึ่งและยังแข็งแกร่งกว่าโหลด yaml ที่โครงสร้างที่ซ้อนกันอย่างล้ำลึก และมีข้อผิดพลาดด้านความปลอดภัยน้อยลงดูที่ yaml.load

=> ในขณะที่ฉันขอขอบคุณตัวชี้ไปยังตัวแปลงที่ใช้ C เท่านั้นฟังก์ชัน byteifyควรเป็นคำตอบเริ่มต้น

สิ่งนี้จะถือเป็นจริงโดยเฉพาะถ้าโครงสร้าง json ของคุณมาจากฟิลด์ซึ่งมีอินพุตของผู้ใช้ แล้วเพราะคุณอาจจะต้องเดินอยู่แล้วมากกว่าโครงสร้างของคุณ - เป็นอิสระเกี่ยวกับโครงสร้างข้อมูลภายในที่คุณต้องการ ( 'Unicode แซนวิช' หรือสตริงไบต์เท่านั้น)

ทำไม?

Unicode ฟื้นฟู สำหรับไม่รู้: ใช้ยาแก้ปวดและอ่านนี้

ดังนั้นการใช้การเรียกซ้ำแบบ byteify คุณจึงฆ่านกสองตัวด้วยหินก้อนเดียว:

  1. รับบททดสอบของคุณจากการทิ้ง json ที่ซ้อนกัน
  2. รับค่าอินพุตของผู้ใช้ที่ทำให้เป็นมาตรฐานเพื่อให้คุณค้นหาสิ่งต่าง ๆ ในที่เก็บข้อมูลของคุณ

ในการทดสอบของฉันปรากฎว่าการแทนที่ input.encode ('utf-8') ด้วย unicodedata.normalize ('NFC', อินพุต) .encode ('utf-8') เร็วกว่า w / o NFC แต่ นั้นขึ้นอยู่กับข้อมูลตัวอย่างที่ฉันเดา


3

gotcha นั้นเป็นsimplejsonและjsonเป็นสองโมดูลที่แตกต่างกันอย่างน้อยก็ในลักษณะที่พวกเขาจัดการกับยูนิโค้ด คุณjsonอยู่ใน py 2.6+ และสิ่งนี้จะให้ค่ายูนิโค้ดแก่คุณในขณะที่simplejsonคืนค่าวัตถุสตริง ลองใช้ easy_install-ing simplejson ในสภาพแวดล้อมของคุณและดูว่าใช้งานได้หรือไม่ มันทำเพื่อฉัน


2

เพียงใช้ดองแทน json สำหรับการถ่ายโอนข้อมูลและโหลดเช่น:

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

ผลลัพธ์ที่ได้คือ (สตริงและจำนวนเต็มได้รับการจัดการอย่างถูกต้อง):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}

1
+1 สำหรับโซลูชันที่ไม่ต้องใช้แพ็คเกจเพิ่มเติม (เช่นyaml ) แต่บางครั้ง - เช่นในกรณีดั้งเดิมของฉัน - ฉันต้องมีข้อมูลใน JSON ดังนั้นดองจึงไม่ใช่ตัวเลือกที่ดีที่สุดเสมอไป นอกจากนี้คุณมีsafe_loadใน YAML ผมไม่ทราบว่าสิ่งที่มีอยู่คล้ายกันสำหรับดอง
Brutus

1

ดังนั้นฉันพบปัญหาเดียวกัน คาดเดาสิ่งที่เป็นผลลัพธ์แรกของ Google

เนื่องจากฉันต้องการส่งผ่านข้อมูลทั้งหมดไปยัง PyGTK สตริง unicode จึงไม่มีประโยชน์สำหรับฉันเช่นกัน ดังนั้นฉันจึงมีวิธีการแปลงแบบเรียกซ้ำอีกวิธีหนึ่ง จริงๆแล้วมันยังจำเป็นสำหรับการแปลง typesafe JSON - json.dump () จะประกันตัวที่ไม่ใช่ตัวอักษรใด ๆ เช่นวัตถุ Python ไม่แปลงดัชนี dict

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj

ปัญหาเดียวที่อาจเกิดขึ้นที่นี่คือถ้าคุณต้องการคีย์ในพจนานุกรมที่แปลงจาก Unicode แม้ว่าการใช้งานนี้จะแปลงค่า แต่ยังคงคีย์ยูนิโคดไว้ หากคุณสร้าง 'newobj' ให้ใช้ newobj [str (i)] = ... และกำหนด obj = newobj เมื่อดำเนินการเสร็จสิ้นคีย์จะถูกแปลงเช่นกัน
Neal Stublen

ซึ่งอาจดีกว่าด้วยความเข้าใจหรือดีกว่าโดยการแปลงคีย์ นอกจากนี้ยังไร้ความรู้สึก; มันทั้งสองกลายพันธุ์วัตถุในสถานที่ (ในกรณีของพจนานุกรม) และส่งกลับค่าใหม่ซึ่งไม่สอดคล้องกับวิธีการรวบรวมในตัวของ Python ซึ่งทั้งสองกลายพันธุ์วัตถุปัจจุบันหรือกลับมาใหม่ แต่ไม่ใช่ทั้งสองอย่าง
Mark Amery

1

ฉันมี JSON dict เป็นสตริง คีย์และค่าเป็นวัตถุ Unicode เช่นในตัวอย่างต่อไปนี้:

myStringDict = "{u'key':u'value'}"

ฉันสามารถใช้byteifyฟังก์ชั่นที่แนะนำข้างต้นโดยการแปลงสตริงไปยังวัตถุที่ใช้dictast.literal_eval(myStringDict)


ตัวอย่างที่คุณให้มาไม่ใช่ตัวอย่างของ JSON {u'key':u'value'}ไม่ใช่ JSON
Mark Amery

2
ฉันรู้อย่างสมบูรณ์แบบว่าไม่ใช่ JSON นั่นเป็นวิธีที่แยกวิเคราะห์จากแหล่งภายนอกในสคริปต์ของฉัน หากเป็น JSON โดยตรงในตัวอย่างต่อไปนี้ฉันไม่ต้องการฟังก์ชัน byteify ที่ระบุว่าเป็นโซลูชัน: {"firstName": "John", "lastName": "Doe"} มันจะดีถ้าก่อนที่จะลงคะแนนคุณอ่านคำตอบ ขอบคุณ
narko

1

สนับสนุน Python2 และ 3 โดยใช้ hook (จากhttps://stackoverflow.com/a/33571117/558397 )

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) for item in data ]

    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

ผลตอบแทน:

 {'three': '', 'key': 'value', 'one': 'two'}

0

เกมนี้มาสาย แต่ฉันสร้างลูกล้อแบบเรียกซ้ำ มันเหมาะกับความต้องการของฉันและฉันคิดว่ามันค่อนข้างสมบูรณ์ มันอาจช่วยคุณได้

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

เพียงผ่านมันเป็นวัตถุ JSON ดังนี้:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

ฉันมีมันเป็นสมาชิกส่วนตัวของชั้นเรียน แต่คุณสามารถเปลี่ยนวิธีการตามที่เห็นสมควร


ฉันพบปัญหาที่ฉันพยายามแยก JSON และส่งการแมปผลลัพธ์ไปยังฟังก์ชันเป็น ** kwargs ดูเหมือนว่าชื่อพารามิเตอร์ฟังก์ชั่นไม่สามารถ unicode ดังนั้นฟังก์ชัน _parseJSON ของคุณจึงยอดเยี่ยม หากมีวิธีที่ง่ายกว่าใครบางคนสามารถแจ้งให้เราทราบ
Neal Stublen

1
รหัสนี้มีปัญหา - คุณโทรซ้ำในรายการชิ้นส่วนซึ่งจะล้มเหลวหากองค์ประกอบของรายการไม่ใช่พจนานุกรม
I82Much

นอกเหนือจากข้อผิดพลาดที่อธิบายโดย @ I82Much นี่เป็นชื่อที่ไม่ดี (จริงๆแล้วมันไม่ได้แยก JSON; json.loadsจำเป็นต้องมีการโทรก่อน) พยายามที่จะแปลงสตริงเป็น ints โดยไม่มีเหตุผลอธิบายและไม่ได้คัดลอกและ วางพร้อม
Mark Amery

0

ฉันเขียน _parse_json () ของ Wells เพื่อจัดการกรณีที่วัตถุ json เองเป็นอาร์เรย์ (กรณีการใช้งานของฉัน)

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj

0

นี่คือตัวเข้ารหัสแบบเรียกซ้ำที่เขียนใน C: https://github.com/axiros/nested_encode

ประสิทธิภาพค่าโสหุ้ยสำหรับโครงสร้าง "เฉลี่ย" ประมาณ 10% เมื่อเทียบกับ json.loads

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

ใช้ teststructure นี้:

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)

0

ด้วย Python 3.6 บางครั้งฉันก็ยังพบปัญหานี้อยู่ ตัวอย่างเช่นเมื่อรับการตอบสนองจาก REST API และโหลดข้อความตอบกลับไปยัง JSON ฉันยังคงได้รับสตริง Unicode พบวิธีแก้ไขปัญหาอย่างง่าย ๆ โดยใช้ json.dumps ()

response_message = json.loads(json.dumps(response.text))
print(response_message)

-1

ฉันพบปัญหานี้เช่นกันและเมื่อต้องจัดการกับ JSON ฉันก็พบว่ามีลูปขนาดเล็กที่แปลงคีย์ยูนิโค้ดเป็นสตริง ( simplejsonบน GAE จะไม่ส่งคืนคีย์สตริง)

obj เป็นวัตถุที่ถอดรหัสจาก JSON:

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargsคือสิ่งที่ฉันส่งให้ผู้สร้างแอปพลิเคชัน GAE (ซึ่งไม่ชอบunicodeคีย์**kwargs)

ไม่แข็งแกร่งเท่าโซลูชันจาก Wells แต่มีขนาดเล็กกว่ามาก


-1

ฉันได้ดัดแปลงรหัสจากคำตอบของMark Ameryโดยเฉพาะอย่างยิ่งเพื่อกำจัดisinstanceข้อดีของการพิมพ์เป็ด

การเข้ารหัสเสร็จสิ้นด้วยตนเองและensure_asciiถูกปิดการใช้งาน หลาม docs สำหรับjson.dumpบอกว่า

หากแน่ใจว่า _ascii เป็น True (ค่าเริ่มต้น) อักขระที่ไม่ใช่ ASCII ทั้งหมดในเอาต์พุตจะถูก Escape ด้วยลำดับ \ uXXXX

คำเตือน: ในหลักคำสอนฉันใช้ภาษาฮังการี การเข้ารหัสอักขระที่เกี่ยวข้องกับฮังการีบางประการ ได้แก่ การเข้ารหัสcp852IBM / OEM ที่ใช้เช่น ใน DOS (บางครั้งเรียกว่าasciiอย่างไม่ถูกต้องฉันคิดว่ามันขึ้นอยู่กับการตั้งค่าเพจรหัส ) cp1250ใช้เช่น ใน Windows (บางครั้งเรียกว่าansiขึ้นอยู่กับการตั้งค่า locale) และiso-8859-2บางครั้งใช้บนเซิร์ฟเวอร์ http ข้อความทดสอบTüskéshátú kígyóbűvölőประกอบกับKoltai László (แบบชื่อบุคคลพื้นเมือง) และอยู่ห่างจากวิกิพีเดีย

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

ฉันต้องการเน้นคำตอบของJarret Hardieซึ่งอ้างอิงถึงข้อมูลจำเพาะ JSONด้วย:

สตริงคือชุดของอักขระ Unicode ที่เป็นศูนย์หรือมากกว่า

ในกรณีการใช้งานของฉันฉันมีไฟล์ที่มี json พวกเขาเป็นutf-8ไฟล์ที่เข้ารหัสensure_asciiผลลัพธ์ในไฟล์ json ที่ถูก Escape แต่ไม่สามารถอ่านได้มากนั่นคือเหตุผลที่ฉันปรับคำตอบของ Mark Amery ให้เหมาะกับความต้องการของฉัน

คำสอนนั้นไม่ได้คิดอย่างถี่ถ้วน แต่ฉันแบ่งปันรหัสด้วยความหวังว่ามันจะมีประโยชน์สำหรับใครบางคน


ฉันไม่แน่ใจว่าฉันเห็นประโยชน์ของการใช้การพิมพ์เป็ดที่นี่หรือไม่ เรารู้ว่าคอลเล็กชันที่ส่งคืนจากjson.loadsนั้นจะเป็นรายการหรือ dicts ไม่ใช่ประเภทที่ผู้ใช้กำหนดเองหรือที่กำหนดโดยไลบรารีที่ใช้วิธีการและวิธีการทางเวทมนตร์ดังนั้นทำไมไม่เพียงแค่isinstanceตรวจสอบ? ไม่เข้าใจง่ายกว่าการตรวจสอบว่ามีอยู่iteritemsหรือiterจะยอมรับวัตถุเป็นอาร์กิวเมนต์หรือไม่
Mark Amery

@ MarkAmery เรื่องนี้เกี่ยวกับการทิ้งไม่ใช่การโหลด ถ้าคุณสร้างข้อมูลเพื่อถ่ายโอนข้อมูลซึ่งต่างจากการโหลดคุณไม่สามารถแน่ใจได้ว่ามันคืออะไร ความคิดคือการปล่อยให้มันมาจากที่ใดก็ได้ในรหัส
n611x007

-2

ตรวจสอบนี้คำตอบของคำถามที่คล้ายกันเช่นนี้ซึ่งระบุว่า

คำนำหน้าเพียงหมายความว่าคุณมีสตริง Unicode เมื่อคุณใช้สตริงสตริงนั้นจะไม่ปรากฏในข้อมูลของคุณ อย่าถูกโยนโดยเอาท์พุทที่พิมพ์ออกมา

ตัวอย่างเช่นลองสิ่งนี้:

print mail_accounts[0]["i"]

คุณจะไม่เห็นคุณ


ไม่เป็นความจริงหากคุณต้องการจัดรูปแบบบางอย่างที่มีสตริง Unicode ใน Py2 เช่น'{}'.format({u'x' : u'y'})ยังมีของคุณอยู่
Ponkadoodle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.