38 คะแนน โดย GN⁺ 2025-12-05 | 10 ความคิดเห็น | แชร์ทาง WhatsApp
  • JSON ซึ่งกลายเป็นมาตรฐานของเว็บ API นั้นอ่านง่ายและยืดหยุ่น แต่มีข้อจำกัดด้านประสิทธิภาพและความเสถียร
  • Protobuf (Protocol Buffers) รับประกันโครงสร้างข้อมูลได้อย่างชัดเจนผ่าน การกำหนดชนิดข้อมูลที่เข้มงวด และ การสร้างโค้ดอัตโนมัติ
  • ใช้ การซีเรียลไลซ์แบบไบนารี ทำให้ ลดขนาดข้อมูลและเพิ่มความเร็วในการส่งได้ มากกว่า JSON ราว 3 เท่า
  • เซิร์ฟเวอร์และไคลเอนต์แชร์ สคีมา .proto เดียวกัน จึงไม่ต้องกังวลเรื่องชนิดข้อมูลไม่ตรงกันหรือการตรวจสอบแบบแมนนวล
  • แม้จะดีบักได้ยากกว่า แต่ในด้าน ประสิทธิภาพ การดูแลรักษา และประสิทธิผลในการพัฒนา Protobuf เหมาะกับ API ยุคใหม่มากกว่า

ความแพร่หลายและข้อจำกัดของ JSON

  • JSON เป็น ฟอร์แมตข้อความที่มนุษย์อ่านได้ง่าย และสามารถตรวจดูข้อมูลได้ด้วย console.log() แบบง่าย ๆ
  • ด้วย การทำงานร่วมกับเว็บได้อย่างสมบูรณ์ จึงถูกใช้อย่างแพร่หลายใน JavaScript และเฟรมเวิร์กฝั่งแบ็กเอนด์จำนวนมาก
  • มอบ ความยืดหยุ่น ในการเพิ่ม-ลบฟิลด์หรือเปลี่ยนชนิดข้อมูลได้อย่างอิสระ แต่ก็ทำให้มีโอกาสเกิดโครงสร้างไม่ตรงกันหรือข้อผิดพลาดได้
  • มี ecosystem ของเครื่องมือที่สมบูรณ์ ทำให้จัดการได้ง่ายแม้มีเพียง text editor หรือ curl
  • อย่างไรก็ตาม แม้จะมีข้อดีเหล่านี้ ก็ยังมีทางเลือกที่ดีกว่าในด้าน ประสิทธิภาพและความปลอดภัยของชนิดข้อมูล

ภาพรวมของ Protobuf

  • เป็น ฟอร์แมตการซีเรียลไลซ์แบบไบนารี ที่ Google พัฒนาขึ้นในปี 2001 และเปิดเผยสู่สาธารณะในปี 2008
  • ถูกใช้อย่างแพร่หลายภายในระบบและในการ สื่อสารระหว่างไมโครเซอร์วิส
  • มักมีความเข้าใจผิดว่า ต้องใช้ร่วมกับ gRPC เสมอ แต่จริง ๆ แล้ว Protobuf สามารถใช้กับ HTTP API ได้โดยอิสระ
  • ในช่วงแรกเข้าถึงได้ยากเพราะ ฟอร์แมตไบนารีมองไม่เห็นได้โดยตรง แต่มีจุดแข็งมากในด้านประสิทธิภาพและความเสถียร

ระบบชนิดข้อมูลที่แข็งแรงและการสร้างโค้ด

  • Protobuf ใช้ไฟล์ .proto เพื่อ กำหนดโครงสร้างข้อมูลอย่างชัดเจน
    • แต่ละฟิลด์มี ชนิดข้อมูลที่เข้มงวด, ตัวระบุแบบตัวเลข และ ชื่อที่กำหนดตายตัว
  • ตัวอย่าง:
    message User {
      int32 id = 1;
      string name = 2;
      string email = 3;
      bool isActive = 4;
    }
    
  • คำสั่ง protoc รองรับ การสร้างโค้ดอัตโนมัติ สำหรับหลายภาษา เช่น Dart, TypeScript, Kotlin, Swift, C#, Go, Rust
  • ใช้โค้ดที่สร้างขึ้นเพื่อทำ การซีเรียลไลซ์ (writeToBuffer) และดีซีเรียลไลซ์ (fromBuffer) ได้โดยตรง จึง ไม่ต้องพาร์สหรือเขียนการตรวจสอบเอง
  • ส่งผลให้ได้ทั้ง การประหยัดเวลา และ การดูแลรักษาที่ดีขึ้น

ประสิทธิภาพของการซีเรียลไลซ์แบบไบนารี

  • Protobuf ซีเรียลไลซ์เป็น ข้อมูลไบนารีแทนข้อความ จึง กะทัดรัดและรวดเร็วมาก
  • เปรียบเทียบขนาดของข้อมูลเดียวกัน (ออบเจ็กต์ User):
    • JSON: 86 ไบต์ (68 ไบต์เมื่อเอาช่องว่างออก)
    • Protobuf: 30 ไบต์
  • สาเหตุของประสิทธิภาพ:
    • ใช้ varint encoding กับตัวเลข
    • ใช้ แท็กตัวเลขแทนคีย์ข้อความ
    • ตัดช่องว่างและไวยากรณ์ที่ไม่จำเป็นออก
    • มี การปรับให้เหมาะกับฟิลด์แบบเลือกได้
  • ผลลัพธ์คือช่วย ลดแบนด์วิดท์, เพิ่มความเร็วในการตอบสนอง, ประหยัดดาต้ามือถือ และ ปรับปรุงประสบการณ์ผู้ใช้

ตัวอย่าง Protobuf API บนพื้นฐาน Dart

  • ใช้แพ็กเกจ shelf เพื่อสร้าง HTTP server แบบง่าย และส่งคืนออบเจ็กต์ User ในรูปแบบ Protobuf
  • แกนหลักของโค้ดฝั่งเซิร์ฟเวอร์:
    • สร้างออบเจ็กต์ User() แล้วซีเรียลไลซ์ด้วย writeToBuffer()
    • กำหนด response header เป็น 'content-type': 'application/protobuf'
  • ฝั่งไคลเอนต์ใช้แพ็กเกจ http และ user.pb.dart เพื่อ ดีโค้ดข้อมูล Protobuf ได้โดยตรง
  • เนื่องจากเซิร์ฟเวอร์และไคลเอนต์แชร์สคีมา .proto เดียวกัน จึง ไม่เกิดความไม่ตรงกันของโครงสร้างข้อมูล
  • แนวทางเดียวกันนี้ยังใช้ได้เหมือนกันใน Go, Rust, Kotlin, Swift, C#, TypeScript เป็นต้น

ข้อดีที่ JSON ยังมีอยู่

  • Protobuf ตีความความหมายได้ยากหากไม่มีสคีมา
    • เพราะจะแสดงเป็นเพียงตัวระบุแบบตัวเลขแทนชื่อฟิลด์ จึง ไม่ง่ายสำหรับมนุษย์ในการอ่าน
  • ตัวอย่างเปรียบเทียบ:
    • JSON: { "id": 42, "name": "Alice" }
    • Protobuf: 1: 42, 2: "Alice"
  • ดังนั้น Protobuf จึง:
    • ต้องมี เครื่องมือสำหรับดีโค้ดโดยเฉพาะ
    • ต้องมี การจัดการสคีมาและเวอร์ชัน อย่างจริงจัง
  • ถึงอย่างนั้น ข้อดีด้านประสิทธิภาพและความมีประสิทธิผลก็ยังมากกว่ามาก

บทสรุป

  • Protobuf เป็นเทคโนโลยีการซีเรียลไลซ์ที่ สุกงอมและประสิทธิภาพสูง ซึ่ง ใช้งานกับ public API ได้อย่างเพียงพอ
  • สามารถทำงานได้อย่างอิสระบน HTTP API ทั่วไป โดยไม่ต้องใช้ gRPC
  • เป็นเครื่องมือที่ช่วยยกระดับทั้ง ประสิทธิภาพ ความทนทาน การลดข้อผิดพลาด และประสิทธิผลในการพัฒนา
  • สำหรับโปรเจกต์รุ่นถัดไป Protobuf มีคุณค่ามากพอที่จะนำมาใช้

10 ความคิดเห็น

 
GN⁺ 2025-12-05
ความเห็นจาก Hacker News
  • JSON มักลงเอยด้วยการส่ง ข้อมูลที่กำกวมหรือไม่รับประกันความถูกต้อง ปัญหาเกิดได้หลายแบบ เช่น ฟิลด์หาย ชนิดข้อมูลผิด คีย์สะกดผิด หรือโครงสร้างที่ไม่ได้มีเอกสารกำกับ เคยมีบทความที่อ้างว่า Protobuf ทำให้เรื่องแบบนี้เป็นไปไม่ได้ เพราะนิยามโครงสร้างข้อความไว้อย่างชัดเจนด้วยไฟล์ .proto แต่จริง ๆ แล้วนี่คือการเข้าใจแนวคิดของ Protobuf ผิดไป ใน proto3 นั้น ไม่รองรับ required field ตั้งแต่แรก เอกสารทางการ (Protobuf Best Practices) ก็ระบุชัดว่า “required fields were harmful and removed” สุดท้ายแล้วฝั่งไคลเอนต์ของ Protobuf ก็ยังต้องเขียนแบบป้องกันตัวเองเหมือน JSON API อยู่ดี

    • บล็อกนั้นมีความเข้าใจผิดคล้ายกันหลายจุด ตัวอย่างเช่นบทความที่คัดค้านการใช้ SVG ก็ไม่ได้คำนึงถึงข้อดีของ การสเกลได้อย่างอิสระของฟอร์แมตเวกเตอร์
    • แก่นของปัญหาคือความต่างของภาษาและการติดตั้งใช้งานฝั่งไคลเอนต์/เซิร์ฟเวอร์เท่านั้น ฉันใช้เฟรมเวิร์ก Gooey ที่ฝั่งไคลเอนต์โดยอาศัยแนวคิด Marshalling ของ Go ถ้าจัดการข้อจำกัดของ Go ได้ ก็ใช้งานแบบ type-safe ได้มากทีเดียว แต่สิ่งสำคัญคือต้องกัน private field ด้วย json:"-" โปรเจ็กต์ของฉันดูได้ที่ Gooey
    • บทความนี้กำลังสับสนระหว่าง ฟอร์แมตการ serialize กับแนวคิดเรื่อง contract
    • ในระบบเครือข่าย ปัญหา ข้อมูลคลาดเคลื่อนกัน (skew) มีอยู่เสมอไม่ว่าจะใช้วิธีเข้ารหัสแบบไหน ต่างกันตรงที่ Protobuf ให้อ็อบเจ็กต์แบบ static type หลังการ decode ส่วน JSON ก็ตรวจสอบได้เหมือนกัน แต่คนส่วนใหญ่มักไม่ทำ สุดท้ายอ็อบเจ็กต์ JSON ก็ถูกแปลงไปมา จนไม่มีใครมั่นใจโครงสร้างภายในได้อีก
    • ผู้เขียนต้นฉบับน่าจะเพียงต้องการจะบอกว่า ใน Protobuf ฟิลด์ที่หายไปจะถูกตั้งเป็นค่าเริ่มต้น ซึ่งไม่เหมือนกับแนวคิดของฟิลด์แบบ “required”
  • JSON ที่ถูกบีบอัดแล้วใช้งานได้ดีพอสมควร และมี ต้นทุนการสื่อสารช่วงเริ่มต้นต่ำ แน่นอนว่าถ้าฟิลด์หายหรือชนิดข้อมูลเปลี่ยนก็มีปัญหา แต่คนที่พยายามออกแบบโครงสร้างที่ type ครบถ้วนสมบูรณ์และสร้างกระบวนการซิงก์เวอร์ชันนั้น ส่วนใหญ่มักล้มเหลว สุดท้ายแล้ว ทางเลือกที่มีต้นทุนฝั่งมนุษย์ต่ำกว่าจะชนะ เพราะงั้น JSON จะยังไม่หายไป จนกว่าจะมีตัวแทนที่มีต้นทุนการสื่อสารระหว่างคนต่ำกว่านี้

    • เห็นด้วย สถาปนิกส่วนใหญ่จะไม่พิจารณา proto หากไม่มีความจำเป็นชัดเจนแบบ gRPC และตราบใดที่ยังไม่มีทางเลือกที่ debug ได้ตรง ๆ ด้วย console.log() JSON ก็จะยังไม่ถูกแทนที่
    • การดีบักก็เป็นจุดแข็งของ JSON เช่นกัน แค่เปิดดูก็อ่านได้เลย ในขณะที่ Protobuf ต้องมี tooling
    • จริง แต่คนมักเลือกไม่ใช้เวลาเพิ่ม 15 นาทีในขั้นออกแบบ แล้วไปเสียเวลา 3 เดือนย้อนรอยปัญหาในภายหลัง
    • JSON คงไม่หายไปหมดเหมือน COBOL แต่สำหรับ โปรเจ็กต์ใหม่ ก็ไม่มีเหตุผลมากนักที่จะต้องใช้มัน
  • Protobuf ไม่ได้สมบูรณ์แบบ ถ้าเซิร์ฟเวอร์กับไคลเอนต์ถูก deploy คนละช่วงเวลาแล้ว เวอร์ชันของสเปกไม่ตรงกัน ความปลอดภัยก็พังได้ แม้จะบรรเทาได้ด้วยการห้าม reuse ID หรือการคัดลอก unknown-field แต่ระบบกระจายตัวนั้นซับซ้อนโดยธรรมชาติ ถึงอย่างนั้น protobuf3 ก็แก้ปัญหาหลายอย่างของ protobuf2 ไปมาก แต่ก่อนแยกไม่ออกว่าค่า default ถูกตั้งไว้หรือฟิลด์นั้นหายไป ตอนนี้แก้ได้ด้วยการใช้ชนิด message

    • ไม่ว่าจะเป็น JSON หรือ Protobuf ก็ต้อง บังคับทดสอบความเข้ากันได้ของเวอร์ชันใน CI pipeline ถึงจะปลอดภัย
    • ไม่มี type system ไหนที่ ไม่พังเมื่อผ่านเครือข่ายไปแล้ว
  • ในบทความบอกว่า “มีประสิทธิภาพสูงมาก” แต่ไม่ได้พูดถึง gzip เลย ข้อมูลข้อความส่วนใหญ่ทุกวันนี้ถูก บีบอัดอัตโนมัติก่อนส่งอยู่แล้ว เพราะงั้น Protobuf ควรถูกเปรียบเทียบกับ JSON ที่บีบอัดด้วย gzip

    • ฉันทดสอบฟอร์แมตไบนารีหลายแบบมาแล้ว แต่สุดท้าย gzipped JSON มีประสิทธิภาพเหนือกว่าแบบทิ้งห่าง
    • ข้อเสียของ JSON คือความเร็วในการ serialize/deserialize ส่วนอย่างอื่นค่อย ๆ แก้ได้
    • JSON/HTML แบบสตรีมมิงร่วมกับ Brotli/zstd ก็น่าสนใจ เพราะใช้ประโยชน์จาก compression window ระหว่างที่ยังคงการเชื่อมต่อไว้ได้
    • ดูเพิ่มเติมได้ที่: บทความเปรียบเทียบประสิทธิภาพ Protobuf ของ Auth0
    • การจับคู่ระหว่าง JSON กับ mod_deflate ให้ ความต่างที่สัมผัสได้ชัดมาก
  • การสนับสนุนโปรโตคอลที่ดีกว่าเป็นเรื่องดี แต่ก็ยากจะบอกว่า Protobuf มาแทน JSON ได้ทั้งด้านประสิทธิภาพและการใช้งาน เพราะ Protobuf มีสคีมาที่เข้มงวดจนพลาดพื้นที่ที่ JSON ทำได้ดี ตรงกันข้าม CBOR อาจเหมาะกว่าในฐานะตัวแทนของ JSON เพราะยืดหยุ่นแบบ JSON แต่เข้ารหัสได้กระชับกว่า

    • แต่ สคีมาที่เข้มงวด ของ Protobuf ก็อาจเป็นข้อดีได้เหมือนกัน เพราะ API ส่วนใหญ่ไม่ได้เผยแพร่ JSON schema ฉันเคยใช้ ajv หรือ superstruct ในการตรวจสอบ แต่กับ Protobuf ไม่จำเป็นต้องทำแบบนั้น
    • คงจะดีถ้าเบราว์เซอร์ รองรับ CBOR API โดยตรง เพราะภายในก็มี implementation อยู่แล้ว ไม่น่าจะยากนัก
  • ASN.1 ในปี 1984 ทำสิ่งที่ Protobuf ทำได้อยู่แล้ว และยืดหยุ่นกว่าด้วย ถ้าใช้การเข้ารหัสแบบ DER ก็ไม่ได้แย่อะไร ดูได้จาก ตัวอย่าง ASN.1 DER Protobuf นั้น ซับซ้อนเกินไปเมื่อเทียบกับสิ่งที่มันทำได้จริง

    • ASN.1 มีฟีเจอร์มากเกินไป ถ้ารองรับทั้งหมดก็จะกลายเป็น ไลบรารีที่ซับซ้อนเกินจำเป็น แต่ถ้ารองรับเพียงบางส่วน มันก็ไม่ใช่ ASN.1 มาตรฐานอีกต่อไป
    • ฉันชอบ ASN.1 DER มากกว่า และเคยปล่อยตัวเข้ารหัส/ถอดรหัส DER ที่เขียนด้วย C เองเป็น FOSS พร้อมสร้าง “ASN.1X” แบบขยายที่ครอบคลุม data model ของ JSON ได้ครบถ้วน
    • แต่ในระบบอย่าง SNMP ความยืดหยุ่นที่มากเกินไป ของ ASN.1 กลับเป็นปัญหา เพราะผู้ผลิตแต่ละรายขยายกันตามใจ
    • แม้แต่ภายใน Google เองก็ใช้ CPU ไปมากกับ การ serialize/deserialize ของ Protobuf
    • ASN.1 ถูกออกแบบเกินความจำเป็น (overengineered) จึงรองรับได้ยาก ฟีเจอร์อย่าง inheritance นั้นไม่จำเป็น
  • ฉันเคยสร้างระบบ production ทั้งชุดด้วย Protobuf และพบว่า การดูแลจัดการนั้นทรมานมาก ในเชิงเทคนิคมันดูดี แต่พอใช้จริง JSON เรียบง่ายกว่ามาก

    • เรื่อง ความอ่านง่ายและการดีบักที่สะดวก ของ JSON นั้นประเมินต่ำไปไม่ได้ ทีมส่วนใหญ่มักเลือก JSON เพื่อประสิทธิภาพระยะสั้น
    • อยากรู้ว่าเจอปัญหาอะไรบ้าง เพราะจากประสบการณ์ของฉัน ความไม่สะดวกของ Protobuf ยังน้อยกว่าความเสี่ยงเรื่อง ข้อมูลเสียหาย ของ JSON มาก Protobuf จะโดนจับได้ตอนคอมไพล์ แต่ JSON มักไประเบิดใน production
  • Protobuf นั้นยอดเยี่ยม แต่ก็น่าเสียดายที่ไม่รองรับ zero-copy ฟอร์แมตอย่าง Cap’n Proto ช่วยตัดคอขวดเรื่อง serialize/deserialize ได้

    • แต่ในทางปฏิบัติ zero-copy อาจช้ากว่าก็ได้ การคัดลอกข้อมูลที่อยู่ในแคชแทบไม่เสียต้นทุน แต่การต้องจัดการโครงสร้างแบบ dynamic โดยตรงกลับมี overhead ในกรณีส่วนใหญ่ การคัดลอกเพียงครั้งเดียวก็เพียงพอแล้ว
    • นี่เป็นเพียงคำกล่าวจากการตลาดของ Cap’n Proto เท่านั้น ในความเป็นจริง ความต่างด้านประสิทธิภาพแทบไม่มีนัยสำคัญ ทั้งสองฟอร์แมตยังต้องแปลงระหว่าง native type กับ binary อยู่ดี ประสิทธิภาพจึงใกล้เคียงกันตามลักษณะ payload
    • นี่อาจไม่ใช่ปัญหาของฟอร์แมต แต่เป็น ปัญหาของ implementation ของไลบรารี
  • ฉันเคยสร้างเซิร์ฟเวอร์ในโปรเจ็กต์ NodeJS ที่นิยาม API ทั้งหมดด้วย .proto และ ตอบกลับเป็น proto หรือ JSON ตาม Content-Type ซึ่งมีโครงสร้างดีกว่า Swagger มาก เพียงแต่น่าเสียดายที่ Google ไม่ได้ให้ความสามารถนี้มาใน ไลบรารีทางการ และ gRPC ก็ใช้งานลำบากเพราะผูกกับ HTTP/2 อนึ่ง ฉันคิดว่า Text proto คือภาษาคอนฟิกแบบ static ที่ดีที่สุด

    • ถ้าต้องการแบบนั้น Twirp น่าจะเหมาะ เพราะจัดการ Protobuf หรือ JSON บน HTTP ธรรมดาได้
    • ConnectRPC ก็มีแนวทางคล้ายกัน แม้ขอบเขตการรองรับยังไม่ชัดเจนนัก
  • ฟอร์แมตไบนารีในฝันของฉันคือแบบ อิงสคีมา แต่ฝังสคีมาไว้ในตัวข้อความด้วย แบบนี้จะเปิดอ่านตรง ๆ ด้วยปลั๊กอิน vim ได้เลย เวลาจัดการอ็อบเจ็กต์เป็นล้านชิ้น การใส่สคีมาขนาด 1KB ลงในข้อความขนาด 2GB ไม่ใช่ภาระใหญ่โตอะไร

    • ภายใน Google มี ระบบนิเวศ Protobuf แบบฝังสคีมาในตัว อยู่แล้ว ลองดู Riegeli
    • Avro หรือ Yardl ก็มีแนวทางคล้ายกัน
    • แต่ในเว็บเซอร์วิสกลับมักเจอกรณีที่ สคีมามีขนาด 200KB แต่ข้อความมีแค่ 1KB ซึ่งในกรณีนั้นจะไม่มีประสิทธิภาพ
    • Avro ก็ยังเป็นทางเลือกที่ดีอยู่ดี
 
tested 2025-12-09
 
onixboox 2025-12-08

https://msgpack.org/ อันนี้เป็นอย่างไรบ้าง?

 
cosine20 2025-12-08

MessagePack ก็ดีเหมือนกัน

 
savvykang 2025-12-06

ผมคิดว่ามันขัดแย้งกันที่จะอ้างว่าฟอร์แมตที่ไม่มีแม้แต่ตัวถอดรหัสอย่างเป็นทางการไว้ใช้ดีบักนั้นมีความสมบูรณ์成熟

 
vipeen 2025-12-06

"ดีบักยาก แต่"

ไม่ผ่าน

 
jjw9512151 2025-12-05

เครื่องมือทุกอย่างก็เหมือนกัน ไม่มีอะไรที่ใช้ได้สารพัดอย่าง แต่ผมก็คิดว่า Protobuf เป็นเครื่องมือที่ดีมากพอครับ
โดยเฉพาะตอนที่เคยต้องส่งข้อมูลปริมาณมากและมีความถี่สูง (20 ครั้งต่อวินาที) ไปยังหลายภาษา/ไคลเอนต์ในสภาพแวดล้อมแบบ embedded ตอนนั้นใช้ nanopb ได้อย่างเรียบร้อยมากครับ

 
ifmkl 2025-12-05

ถ้าเข้มงวดขนาดนั้น เดี๋ยวก็กลายเป็นส่งมาแบบ XML ใช่ไหมครับ 555

 
click 2025-12-06

ถ้ากำหนดสคีมาด้วย dtd ไว้ แล้วฝั่ง parser ทำ caching ก็เหมือนจะได้ผลเท่ากับส่งสคีมาแค่ครั้งเดียวเหมือนกันนะ

 
bakyeono 2025-12-05
  • รูปแบบไบนารีในอุดมคติที่ผมอยากได้คือเป็นแบบอิงสคีมาและในขณะเดียวกันก็มีสคีมารวมอยู่ในข้อความด้วย แบบนี้ก็จะอ่านได้ทันทีผ่านปลั๊กอิน vim ถ้าต้องจัดการอ็อบเจ็กต์หลายล้านชิ้น การแนบสคีมาขนาด 1KB ไปกับข้อความขนาด 2GB ก็ไม่ใช่ภาระใหญ่แต่อย่างใด
  • แต่ในบริการเว็บกลับมักมีกรณีที่สคีมามีขนาด 200KB แต่ข้อความมีแค่ 1KB ซึ่งในกรณีนี้จะไม่มีประสิทธิภาพ

=> ยังไงก็ต้องส่งสคีมาอย่างน้อย 1 ครั้งอยู่แล้วไม่ใช่หรือ? ต่อให้เป็น JSON ก็ไม่ได้แปลว่าไม่มีสคีมา เพียงแต่สคีมาถูกรวมอยู่ในข้อมูลแบบแฝง ๆ ดังนั้นจึงไม่ใช่ว่าไม่ได้ส่งสคีมาเสียทีเดียว กลับกัน มันยิ่งไม่มีประสิทธิภาพกว่าเพราะส่งสคีมาซ้ำในแต่ละรายการด้วยซ้ำ รูปแบบที่ "อิงสคีมาและในขณะเดียวกันก็มีสคีมารวมอยู่ในข้อความด้วย" ดูจะเป็นไอเดียที่ค่อนข้างดีเลยนะ