การใช้งาน Claude ผ่าน API นั้นมีประสิทธิภาพสูง แต่หลายคนเจอปัญหาในการ parse ผลลัพธ์ที่อยู่ในรูปแบบ XML วันนี้ผมจะมาแชร์ประสบการณ์ตรงในการแก้ไขปัญหา Claude XML output parsing ที่พบบ่อยที่สุด

สถานการณ์ข้อผิดพลาดจริง: เมื่อ Response มาเป็น XML แต่โค้ดพัง

ผมเคยเจอสถานการณ์ที่ทำให้หงุดหงิดมาก: ส่ง request ไปที่ Claude API แล้วได้ response กลับมาเป็น XML ที่มีโครงสร้างซับซ้อน พอลอง parse ด้วยโค้ดเดิมที่ใช้กับ JSON อยู่ กลับเจอ error json.decoder.JSONDecodeError: Expecting value หรือ worse คือ parse ผ่านแต่ได้ข้อมูลผิดเพี้ยน

Claude XML Output คืออะไร

Claude สามารถ output ในรูปแบบ XML tags ได้โดยการกำหนดใน system prompt เช่น:

<?xml version="1.0" encoding="UTF-8"?>
<response>
  <status>success</status>
  <data>
    <user>
      <id>12345</id>
      <name>สมชาย ใจดี</name>
      <email>[email protected]</email>
    </user>
    <metadata>
      <created_at>2024-01-15T10:30:00Z</created_at>
      <role>admin</role>
    </metadata>
  </data>
</response>

การ parse XML ที่ถูกต้องจะช่วยให้คุณดึงข้อมูลได้แม่นยำและรวดเร็ว

การตั้งค่า Claude API ด้วย HolySheep AI

ก่อนจะ parse XML ได้ ต้องเรียก API ให้ได้ response ก่อน สมัครที่นี่ เพื่อรับ API key ฟรี จากนั้นใช้โค้ดต่อไปนี้:

import anthropic
import xml.etree.ElementTree as ET

กำหนดค่า API key และ base URL

client = anthropic.Anthropic( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" # URL สำหรับ HolySheep )

สร้าง system prompt ให้ Claude output เป็น XML

SYSTEM_PROMPT = """คุณจะต้องตอบกลับในรูปแบบ XML เท่านั้น พร้อมใช้ tags ดังนี้: <result>สำหรับคำตอบหลัก</result> <details>สำหรับรายละเอียดเพิ่มเติม</details> <confidence>ค่าความมั่นใจ 0-100</confidence> ห้ามใช้ JSON หรือ markdown code blocks""" def call_claude_with_xml_output(prompt_text): """เรียก Claude และรับ response เป็น XML""" message = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system=SYSTEM_PROMPT, messages=[ {"role": "user", "content": prompt_text} ] ) # ดึงข้อความ response response_text = message.content[0].text return response_text

ทดสอบเรียกใช้

xml_response = call_claude_with_xml_output("อธิบายเรื่อง Machine Learning แบบสั้น") print(xml_response)

การ Parse XML ด้วย ElementTree

หลังจากได้ response เป็น XML string แล้ว ต่อไปคือการ parse ให้ถูกต้อง:

def parse_claude_xml_response(xml_string):
    """Parse XML response จาก Claude อย่างปลอดภัย"""
    
    # วิธีที่ 1: ใช้ ElementTree (มาตรฐาน library)
    try:
        # ลบ XML declaration ถ้ามี
        if xml_string.strip().startswith('<?xml'):
            xml_string = xml_string[xml_string.index('?>')+2:].strip()
        
        root = ET.fromstring(xml_string)
        
        # ดึงข้อมูลจากแต่ละ tag
        result = {
            'result': root.find('result').text if root.find('result') is not None else None,
            'details': root.find('details').text if root.find('details') is not None else None,
            'confidence': root.find('confidence').text if root.find('confidence') is not None else None
        }
        
        return result
        
    except ET.ParseError as e:
        print(f"XML Parse Error: {e}")
        return None

def parse_xml_with_namespaces(xml_string, namespace=None):
    """Parse XML ที่มี namespace ซับซ้อน"""
    
    if namespace:
        ET.register_namespace('', namespace)
    
    try:
        root = ET.fromstring(xml_string)
        
        # ถ้ามี namespace ต้องใช้ ns ในการค้นหา
        if namespace:
            ns = {'ns': namespace}
            result = root.find('.//ns:result', ns)
            details = root.find('.//ns:details', ns)
        else:
            result = root.find('.//result')
            details = root.find('.//details')
            
        return {
            'result': result.text if result is not None else None,
            'details': details.text if details is not None else None
        }
        
    except Exception as e:
        print(f"Error parsing XML: {e}")
        return None

ทดสอบ parse

xml_sample = """ <response> <result>Machine Learning คือการสอนคอมพิวเตอร์ให้เรียนรู้จากข้อมูล</result> <details>ML ใช้ algorithms หลายประเภท เช่น supervised, unsupervised learning</details> <confidence>95</confidence> </response> """ parsed = parse_claude_xml_response(xml_sample) print(f"Result: {parsed['result']}") print(f"Confidence: {parsed['confidence']}%")

การ Parse XML ด้วย BeautifulSoup (ทางเลือก)

from bs4 import BeautifulSoup

def parse_xml_with_beautifulsoup(xml_string):
    """ใช้ BeautifulSoup สำหรับ XML ที่ซับซ้อน"""
    
    soup = BeautifulSoup(xml_string, 'xml')  # ใช้ 'xml' parser
    
    # ดึงข้อมูลจาก nested tags
    response = soup.find('response')
    
    if response:
        return {
            'status': response.get('status', 'unknown'),
            'result': response.result.get_text(strip=True) if response.result else None,
            'details': response.details.get_text(strip=True) if response.details else None,
            'confidence': response.confidence.get_text(strip=True) if response.confidence else None,
            'all_tags': [tag.name for tag in response.find_all()]
        }
    
    return None

def extract_nested_data(xml_string):
    """ดึงข้อมูลจาก XML ที่ซ้อนกันหลายชั้น"""
    
    soup = BeautifulSoup(xml_string, 'xml')
    results = []
    
    # หาทุก items ใน list
    for item in soup.find_all('item'):
        item_data = {
            'id': item.get('id'),
            'name': item.name.text if item.name else None,
            'properties': {}
        }
        
        # ดึง properties ที่ซ้อนกัน
        for prop in item.find_all('property'):
            item_data['properties'][prop.get('key')] = prop.text
            
        results.append(item_data)
    
    return results

ตัวอย่าง XML ที่ซ้อนกัน

nested_xml = """ <data> <items> <item id="1"> <name>Product A</name> <property key="price">299</property> <property key="stock">50</property> </item> <item id="2"> <name>Product B</name> <property key="price">499</property> <property key="stock">25</property> </item> </items> </data> """ items = extract_nested_data(nested_xml) for item in items: print(f"ID: {item['id']}, Name: {item['name']}, Price: {item['properties']['price']}")

การ Validate XML ก่อน Parse

import re
from typing import Tuple, Optional

def validate_xml_structure(xml_string) -> Tuple[bool, Optional[str]]:
    """
    Validate XML structure ก่อน parse
    ป้องกัน error จาก malformed XML
    """
    
    if not xml_string or not xml_string.strip():
        return False, "Empty XML string"
    
    # ตรวจสอบ basic structure
    xml_string = xml_string.strip()
    
    # ตรวจสอบว่ามี root element หรือไม่
    root_pattern = r'<(\w+)(?:\s|>)'
    roots = re.findall(root_pattern, xml_string)
    
    if not roots:
        return False, "No root element found"
    
    # ตรวจสอบ closing tags
    open_tags = re.findall(r'<(\w+)(?:\s[^>]*)?(?<!/)>', xml_string)
    close_tags = re.findall(r'</(\w+)>', xml_string)
    
    # ตรวจสอบ tag balance
    for tag in open_tags:
        if tag not in close_tags:
            return False, f"Unclosed tag: <{tag}>"
    
    # ตรวจสอบ CDATA sections
    cdata_count = xml_string.count('<![CDATA[')
    cdata_close_count = xml_string.count(']]>')
    
    if cdata_count != cdata_close_count:
        return False, "Mismatched CDATA sections"
    
    return True, None

def sanitize_xml_for_parsing(xml_string) -> str:
    """ทำความสะอาด XML ก่อน parse"""
    
    # ลบ XML declaration
    xml_string = re.sub(r'<\?xml[^?]*\?>', '', xml_string)
    
    # ลบ HTML comments
    xml_string = re.sub(r'<!--.*?-->', '', xml_string, flags=re.DOTALL)
    
    # แก้ไข ampersands ที่ไม่ได้ escape
    xml_string = re.sub(r'&(?!amp;|lt;|gt;|quot;|apos;)', '&amp;', xml_string)
    
    # ลบ whitespace ที่ไม่จำเป็น
    xml_string = re.sub(r'\s+', ' ', xml_string)
    
    return xml_string.strip()

ทดสอบ validation

test_xml = "<response><result>Test</result></response>" is_valid, error = validate_xml_structure(test_xml) print(f"Valid: {is_valid}, Error: {error}")

ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข

ข้อผิดพลาดที่ 1: xml.etree.ElementTree.ParseError: unclosed token

# ❌ วิธีที่ผิด: Parse XML ที่มี special characters โดยตรง
import xml.etree.ElementTree as ET

xml_with_amp = "<data><item>Tom & Jerry</item></data>"
try:
    root = ET.fromstring(xml_with_amp)  # Error!
except ET.ParseError as e:
    print(f"Error: {e}")

✅ วิธีที่ถูก: Escape special characters ก่อน

import xml.sax.saxutils as saxutils xml_with_amp = "<data><item>Tom & Jerry</item></data>"

ถ้าได้รับ unescaped string มา

unescaped = "Tom & Jerry" escaped = saxutils.escape(unescaped) xml_safe = f"<data><item>{escaped}</item></data>"

ถ้าได้รับ double-escaped string มา

double_escaped = "Tom &amp; Jerry" unescaped_fixed = double_escaped.replace("&amp;", "&") xml_fixed = f"<data><item>{unescaped_fixed}</item></data>"

หรือใช้ CDATA

def wrap_in_cdata(text): return f"<![CDATA[{text}]]>" xml_cdata = f"<data><item>{wrap_in_cdata('Tom & Jerry & Friends')}</item></data>" root = ET.fromstring(xml_cdata) print(f"Parsed: {root.find('item').text}")

ข้อผิดพลาดที่ 2: AttributeError: 'NoneType' object has no attribute 'text'

# ❌ วิธีที่ผิด: ดึง text จาก tag ที่อาจไม่มีอยู่
xml_data = "<response><result>OK</result></response>"
root = ET.fromstring(xml_data)
confidence = root.find('confidence').text  # AttributeError! ไม่มี confidence tag

✅ วิธีที่ถูก: ตรวจสอบก่อนดึงข้อมูล

def safe_get_text(element, tag_name, default=None): """ดึง text จาก tag อย่างปลอดภัย""" target = element.find(tag_name) if target is not None: return target.text return default root = ET.fromstring(xml_data) confidence = safe_get_text(root, 'confidence', 0) result = safe_get_text(root, 'result', 'No result') details = safe_get_text(root, 'details', '') print(f"Confidence: {confidence}, Result: {result}")

✅ หรือใช้ walrus operator (Python 3.8+)

if (tag := root.find('result')) is not None: print(f"Result: {tag.text}") else: print("Result tag not found")

✅ ใช้ try-except เพื่อ graceful handling

def extract_data_safe(xml_string): try: root = ET.fromstring(xml_string) return { 'result': root.find('result').text if root.find('result') is not None else None, 'confidence': int(root.find('confidence').text) if root.find('confidence') is not None else 0, 'metadata': root.find('metadata/details').text if root.find('metadata/details') is not None else None } except (ET.ParseError, ValueError, AttributeError) as e: print(f"Parse error: {e}") return None

ข้อผิดพลาดที่ 3: ปัญหา Namespace ใน XML Response

# ❌ วิธีที่ผิด: ค้นหา tag โดยไม่สนใจ namespace
xml_ns = """<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns2:Response xmlns:ns2="http://example.com/api">
      <ns2:result>Success</ns2:result>
    </ns2:Response>
  </soap:Body>
</soap:Envelope>
"""

root = ET.fromstring(xml_ns)
result = root.find('result')  # ไม่พบ! เพราะมี namespace

✅ วิธีที่ถูก: กำหนด namespace และใช้ในการค้นหา

namespaces = { 'soap': '