JSON与XML响应处理与验证技术
本文全面介绍了REST Assured框架中JsonPath和XmlPath的核心技术,详细解析了JSON与XML响应处理的语法规则、高级查询技巧、类型安全提取方法以及复杂数据结构的验证策略。涵盖了从基础路径表达式到Groovy高级用法,从简单字段验证到嵌套对象和数组操作的完整技术体系,为API测试提供了全面的响应处理与验证解决方案。
JsonPath表达式语法与使用技巧
JsonPath是REST Assured框架中用于处理JSON响应的强大工具,它基于Groovy的GPath语法,提供了简洁而灵活的方式来查询和提取JSON文档中的数据。掌握JsonPath表达式语法对于高效进行API测试至关重要。
基础路径表达式
JsonPath使用点号.来访问对象的属性,使用方括号[]来访问数组元素:
// 访问对象属性
String category = JsonPath.from(json).getString("store.book[0].category");
// 访问数组元素
List<String> authors = JsonPath.from(json).getList("store.book.author");
// 使用负数索引访问数组末尾元素
String lastBook = JsonPath.from(json).getString("store.book[-1].title");
数组操作与切片
JsonPath支持多种数组操作方式,包括范围选择和条件过滤:
// 获取数组长度
int bookCount = JsonPath.from(json).getInt("store.book.size()");
// 范围选择(前3本书)
List<Map> firstThreeBooks = JsonPath.from(json).get("store.book[0..2]");
// 条件过滤(价格大于10的书)
List<Map> expensiveBooks = JsonPath.from(json).get(
"store.book.findAll { book -> book.price > 10 }"
);
Groovy表达式的高级用法
JsonPath集成了Groovy的强大表达式能力,支持复杂的查询逻辑:
// 多条件过滤
List<Map> fictionBooks = JsonPath.from(json).get(
"store.book.findAll { book -> book.category == 'fiction' && book.price < 15 }"
);
// 使用参数化查询(避免SQL注入风险)
String authorName = "Herman Melville";
List<Map> booksByAuthor = JsonPath.from(json)
.param("author", authorName)
.get("store.book.findAll { book -> book.author == author }");
// 数学运算和聚合函数
double minPrice = JsonPath.from(json).getDouble("store.book.price.min()");
double maxPrice = JsonPath.from(json).getDouble("store.book.price.max()");
double avgPrice = JsonPath.from(json).getDouble("store.book.price.sum() / store.book.size()");
特殊符号和根节点处理
JsonPath提供了一些特殊符号来处理不同的场景:
// 访问根节点
Map<String, Object> root = JsonPath.from(json).get("$");
// 空字符串表示整个文档
Map<String, Object> entireDoc = JsonPath.from(json).get("");
// 无参数get()方法等效于get("")
Map<String, Object> sameAsAbove = JsonPath.from(json).get();
// 处理数字开头的属性名
double value = JsonPath.from(json).getDouble("map.'0'"); // 使用引号包裹数字属性
类型安全的提取方法
JsonPath提供了多种类型安全的提取方法,避免类型转换错误:
// 基本类型提取
int lottoId = JsonPath.from(lottoJson).getInt("lotto.lottoId");
double price = JsonPath.from(json).getDouble("store.book[0].price");
boolean available = JsonPath.from(json).getBoolean("product.available");
// 列表提取(带泛型类型)
List<String> categories = JsonPath.from(json).getList("store.book.category", String.class);
List<Integer> winnerIds = JsonPath.from(lottoJson).getList("lotto.winners.winnerId", Integer.class);
// 对象映射到自定义类
Book book = JsonPath.from(json).getObject("store.book[0]", Book.class);
空值安全和错误处理
JsonPath提供了优雅的空值处理机制:
// 安全访问可能不存在的路径
String missingValue = JsonPath.from(json).getString("store.nonexistent.field"); // 返回null
// 检查路径是否存在
boolean pathExists = JsonPath.from(json).get("store.book") != null;
// 使用默认值处理空值
String category = Optional.ofNullable(
JsonPath.from(json).getString("store.book[10].category")
).orElse("Unknown");
配置选项和性能优化
JsonPath支持多种配置选项来优化性能和行为:
// 配置数字返回类型
JsonPath.config = new JsonPathConfig()
.numberReturnType(JsonPathConfig.NumberReturnType.BIG_DECIMAL);
// 实例级别的配置
JsonPath jsonPath = JsonPath.from(json)
.using(new JsonPathConfig()
.numberReturnType(JsonPathConfig.NumberReturnType.FLOAT_AND_DOUBLE));
// 设置根路径(简化重复路径)
JsonPath simplified = new JsonPath(json).setRootPath("store.book");
List<String> titles = simplified.getList("title");
实际应用示例
以下是一个完整的JSON响应验证示例,展示了JsonPath的综合应用:
@Test
public void testLottoApiResponse() {
String jsonResponse = get("/lotto").asString();
// 基础验证
assertThat(JsonPath.from(jsonResponse).getInt("lotto.lottoId"), equalTo(5));
// 集合验证
List<Integer> winnerIds = JsonPath.from(jsonResponse)
.getList("lotto.winners.winnerId", Integer.class);
assertThat(winnerIds, hasItems(23, 54));
// 复杂条件验证
List<Map> bigWinners = JsonPath.from(jsonResponse)
.get("lotto.winners.findAll { winner -> winner.numbers.size() > 3 }");
assertThat(bigWinners.size(), greaterThan(0));
// 提取特定数据
int firstWinnerId = JsonPath.from(jsonResponse).getInt("lotto.winners[0].winnerId");
List<Integer> winningNumbers = JsonPath.from(jsonResponse)
.getList("lotto.winning-numbers", Integer.class);
}
最佳实践和注意事项
- 性能考虑:复杂的Groovy表达式会影响性能,尽量使用简单路径
- 安全性:避免直接将用户输入拼接到表达式中,使用参数化查询
- 可读性:复杂的表达式应添加注释说明其作用
- 错误处理:总是对可能不存在的路径进行空值检查
- 类型安全:使用类型安全的get方法避免运行时类型转换错误
通过掌握这些JsonPath表达式技巧,您可以更加高效地编写REST API测试用例,提高测试代码的可读性和维护性。
XmlPath XML文档处理详解
XmlPath是REST Assured框架中专门用于处理XML文档的强大工具,它基于Groovy的GPath语法,提供了简洁直观的方式来查询和提取XML数据。与传统的XPath相比,XmlPath更加灵活且易于使用,特别适合在API测试中进行XML响应验证。
XmlPath核心功能特性
XmlPath提供了丰富的功能来处理XML文档,主要包括:
| 功能类别 | 具体方法 | 描述 |
|---|---|---|
| 文档解析 | from(), with(), given() |
从不同来源创建XmlPath实例 |
| 数据提取 | get(), getString(), getInt() |
提取特定路径的数据 |
| 节点操作 | getNode(), getNodeChildren() |
获取XML节点和子节点 |
| 类型转换 | getObject(), getList() |
将XML数据转换为Java对象 |
| 配置管理 | using(), setRootPath() |
配置解析参数和根路径 |
| 调试输出 | peek(), prettyPeek() |
格式化输出XML内容 |
XmlPath基本用法
XmlPath支持多种方式来创建实例,可以根据不同的数据源选择合适的方法:
// 从字符串创建
XmlPath xmlPath = new XmlPath(xmlContent);
XmlPath xmlPath = XmlPath.from(xmlContent);
XmlPath xmlPath = XmlPath.with(xmlContent);
XmlPath xmlPath = XmlPath.given(xmlContent);
// 从文件创建
XmlPath xmlPath = new XmlPath(new File("data.xml"));
XmlPath xmlPath = XmlPath.from(new File("data.xml"));
// 从输入流创建
XmlPath xmlPath = new XmlPath(inputStream);
XmlPath xmlPath = XmlPath.from(inputStream);
XML文档查询语法
XmlPath使用Groovy GPath语法来查询XML文档,语法简洁而强大:
<!-- 示例XML文档 -->
<shopping>
<category type="groceries">
<item>
<name>Chocolate</name>
<price>10</price>
</item>
<item>
<name>Coffee</name>
<price>20</price>
</item>
</category>
<category type="supplies">
<item>
<name>Paper</name>
<price>5</price>
</item>
</category>
</shopping>
// 基本查询示例
String firstName = xmlPath.get("shopping.category.item[0].name"); // "Chocolate"
int itemCount = xmlPath.getInt("shopping.category.item.size()"); // 3
List<String> allNames = xmlPath.getList("shopping.category.item.name"); // ["Chocolate", "Coffee", "Paper"]
高级查询功能
XmlPath支持复杂的高级查询,包括条件过滤、属性访问和集合操作:
// 条件过滤查询
List<Node> expensiveItems = xmlPath.get(
"shopping.category.item.findAll { it.price.toFloat() > 10 }"
);
// 属性访问
String categoryType = xmlPath.get("shopping.category[0].@type"); // "groceries"
List<String> allTypes = xmlPath.get("shopping.category.@type.list()"); // ["groceries", "supplies"]
// 深度搜索
String chocolatePrice = xmlPath.get("**.find { it.name == 'Chocolate' }.price"); // "10"
// 范围查询
List<String> firstTwoItems = xmlPath.get("shopping.category.item[0..1].name"); // ["Chocolate", "Coffee"]
节点操作与类型转换
XmlPath提供了丰富的节点操作方法,可以获取完整的节点结构并进行类型转换:
// 获取节点对象
Node firstCategory = xmlPath.getNode("shopping.category[0]");
NodeChildren allItems = xmlPath.getNodeChildren("shopping.category.item");
// 类型转换
List<Integer> prices = xmlPath.getList("shopping.category.item.price", Integer.class);
CustomObject obj = xmlPath.getObject("shopping.category[0]", CustomObject.class);
// 节点属性操作
Map<String, String> attributes = firstCategory.getAttributes();
String typeAttribute = firstCategory.getAttribute("type");
XmlPath配置选项
XmlPath提供了灵活的配置选项来定制解析行为:
// 自定义配置
XmlPathConfig config = XmlPathConfig.xmlPathConfig()
.charset("UTF-8")
.namespaceAware(true)
.declaredNamespace("ns", "http://example.com/ns")
.disableLoadingOfExternalDtd();
XmlPath customXmlPath = xmlPath.using(config);
// 设置根路径
XmlPath rooted = xmlPath.setRootPath("shopping.category");
String firstName = rooted.get("item[0].name"); // 相对路径查询
// HTML模式解析
XmlPath htmlPath = new XmlPath(XmlPath.CompatibilityMode.HTML, htmlContent);
错误处理与调试
XmlPath提供了完善的错误处理和调试功能:
try {
String value = xmlPath.get("non.existent.path");
} catch (XmlPathException e) {
// 处理路径不存在的情况
}
// 调试输出
xmlPath.prettyPeek(); // 格式化输出整个XML
xmlPath.peek(); // 简洁输出
// 参数化查询(避免SQL注入式问题)
String searchType = "groceries";
List<Node> results = xmlPath.param("type", searchType)
.get("shopping.category.findAll { it.@type == type }");
性能优化建议
在使用XmlPath时,可以考虑以下性能优化策略:
- 重用XmlPath实例:避免重复创建实例,特别是在循环中
- 设置根路径:使用
setRootPath()缩小查询范围 - 批量查询:尽量使用集合操作而不是多次单独查询
- 缓存结果:对于不变的数据,考虑缓存查询结果
flowchart TD
A[XML数据源] --> B[创建XmlPath实例]
B --> C[配置解析参数]
C --> D[执行GPath查询]
D --> E{查询成功?}
E -->|是| F[获取结果数据]
E -->|否| G[处理XmlPath异常]
F --> H[类型转换处理]
H --> I[返回最终结果]
XmlPath作为REST Assured框架中XML处理的核心组件,为开发者提供了强大而灵活的XML文档处理能力。通过掌握其丰富的查询语法和配置选项,可以高效地进行XML数据的提取、验证和转换,大大简化API测试中的XML处理工作。
响应体验证与断言方法
REST Assured 提供了强大而灵活的响应体验证机制,使开发者能够轻松地对 HTTP 响应的各个方面进行断言验证。通过链式调用和 Hamcrest 匹配器的结合,可以构建出既直观又强大的验证逻辑。
基础响应验证方法
REST Assured 的核心验证方法主要通过 then() 方法后的链式调用来实现:
// 状态码验证
get("/api/users").then().statusCode(200);
// 状态行验证
get("/api/users").then().statusLine("HTTP/1.1 200 OK");
// 内容类型验证
get("/api/users").then().contentType(ContentType.JSON);
JSON 响应体验证
对于 JSON 格式的响应,REST Assured 提供了丰富的路径表达式支持:
// 简单字段验证
get("/api/users/1").then()
.body("id", equalTo(1))
.body("name", equalTo("John Doe"))
.body("email", containsString("@example.com"));
// 嵌套对象验证
get("/api/users/1").then()
.body("address.city", equalTo("New York"))
.body("address.zipCode", equalTo("10001"));
// 数组元素验证
get("/api/users").then()
.body("users.size()", equalTo(5))
.body("users[0].id", equalTo(1))
.body("users.name", hasItems("John", "Jane", "Bob"));
XML 响应体验证
对于 XML 响应,可以使用 XPath 表达式进行验证:
// XPath 验证
get("/api/users/1").then()
.body(hasXPath("/user/name[text()='John Doe']"))
.body(hasXPath("/user/email[contains(@domain,'example')]"));
// 命名空间支持
given().config(config().xmlConfig(xmlConfig().declareNamespace("ns", "http://example.com")))
.get("/api/users").then()
.body(hasXPath("/ns:users/ns:user[1]/ns:name"));
响应头验证
响应头信息也可以通过简单的方式进行验证:
get("/api/users").then()
.header("Content-Type", "application/json;charset=UTF-8")
.header("Cache-Control", containsString("max-age"))
.header("X-RateLimit-Limit", equalTo("1000"));
Cookie 验证
Cookie 的验证支持详细的属性检查:
get("/api/login").then()
.cookie("sessionId", notNullValue())
.cookie("sessionId", detailedCookie()
.value(containsString("abc"))
.secured(true)
.httpOnly(true)
.maxAge(3600));
高级验证技巧
1. 多条件组合验证
get("/api/complex").then()
.statusCode(200)
.contentType(ContentType.JSON)
.body("data.result", equalTo("success"))
.body("data.items.size()", greaterThan(0))
.body("data.items[0].status", equalTo("active"))
.header("X-Request-ID", notNullValue());
2. 响应时间验证
get("/api/performance").then()
.time(lessThan(2000L)) // 响应时间小于2秒
.time(between(100L, 500L), TimeUnit.MILLISECONDS);
3. 使用参数化路径验证
get("/api/users/{id}", 123).then()
.body("id", equalTo(123))
.body("name", equalTo("User 123"));
// 使用参数列表
List<Argument> args = Arrays.asList(withArg("user"), withArg("profile"));
get("/api/{type}/{subtype}", "user", "profile").then()
.body("$.%s.%s.name", withArgs(args), equalTo("John Doe"));
验证流程示意图
以下是 REST Assured 响应验证的典型流程:
flowchart TD
A[发送HTTP请求] --> B[接收响应]
B --> C{验证响应状态}
C --> D[状态码验证]
C --> E[状态行验证]
B --> F{验证响应头}
F --> G[Content-Type验证]
F --> H[自定义头验证]
B --> I{验证响应体}
I --> J[JSON路径验证]
I --> K[XPath验证]
I --> L[Hamcrest匹配器验证]
B --> M{验证Cookie}
M --> N[Cookie值验证]
M --> O[Cookie属性验证]
D & E & G & H & J & K & L & N & O --> P[验证结果汇总]
P --> Q{所有验证通过?}
Q -->|是| R[测试通过]
Q -->|否| S[抛出断言异常]
常用 Hamcrest 匹配器
REST Assured 与 Hamcrest 匹配器完美集成,以下是一些常用匹配器:
| 匹配器 | 描述 | 示例 |
|---|---|---|
equalTo() |
等于特定值 | body("id", equalTo(1)) |
containsString() |
包含特定字符串 | body("name", containsString("John")) |
hasItems() |
包含多个项目 | body("users.name", hasItems("John", "Jane")) |
notNullValue() |
不为空 | body("timestamp", notNullValue()) |
greaterThan() |
大于特定值 | body("count", greaterThan(0)) |
lessThan() |
小于特定值 | body("price", lessThan(100.0)) |
hasSize() |
具有特定大小 | body("items", hasSize(5)) |
empty() |
为空 | body("errors", empty()) |
错误处理与调试
当验证失败时,REST Assured 会提供详细的错误信息:
// 启用验证失败时的请求响应日志
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
get("/api/users").then()
.statusCode(200)
.body("users[0].name", equalTo("Expected Name"));
最佳实践建议
- 明确验证目标:在编写验证代码前,明确需要验证的响应部分
- 使用合适的匹配器:根据验证需求选择最合适的 Hamcrest 匹配器
- 组合验证:将相关的验证条件组合在一起,提高代码可读性
- 错误信息清晰:利用
onFailMessage()提供有意义的错误信息 - 性能考虑:避免不必要的重复验证,合理使用根路径设置
通过掌握这些响应体验证与断言方法,您可以构建出健壮、可维护的 API 测试用例,确保接口的稳定性和正确性。
复杂数据结构提取与验证
在现代REST API开发中,复杂的数据结构已成为常态。REST Assured提供了强大的JsonPath功能,能够高效地处理嵌套对象、数组、列表和映射等复杂数据结构。本节将深入探讨如何使用REST Assured提取和验证这些复杂数据结构。
JsonPath基础语法
JsonPath采用类似XPath的语法来导航JSON文档,支持多种操作符和函数:
flowchart TD
A[JsonPath表达式] --> B[基本选择器]
A --> C[数组索引]
A --> D[过滤器表达式]
A --> E[函数操作]
B --> B1[.property]
B --> B2[..property]
B --> B3['property']
C --> C1[array[index]]
C --> C2[array[-1]]
C --> C3[array[0:2]]
D --> D1[?(@.price > 10)]
D --> D2[?(@.name =~ /.*Tolkien/)]
E --> E1[length()]
E --> E2[keys()]
E --> E3[values()]
复杂数据结构的提取方法
1. 列表和数组提取
对于包含数组的JSON响应,可以使用getList()方法提取整个列表:
// 提取字符串列表
List<String> categories = response.jsonPath().getList("store.book.category");
// 提取特定类型的列表
List<Integer> prices = response.jsonPath().getList("store.book.price", Integer.class);
// 使用Groovy表达式过滤列表
List<Map<String, Object>> expensiveBooks = response.jsonPath()
.getList("store.book.findAll { it.price > 15 }");
2. 映射和对象提取
对于嵌套对象结构,可以使用getMap()和getObject()方法:
// 提取Map结构
Map<String, Object> book = response.jsonPath().getMap("store.book[0]");
// 提取特定类型的Map
Map<String, String> bookDetails = response.jsonPath()
.getMap("store.book[0]", String.class, String.class);
// 将JSON对象反序列化为Java对象
Book bookObject = response.jsonPath().getObject("store.book[0]", Book.class);
3. 深度嵌套结构处理
对于多层嵌套的复杂结构,JsonPath提供了强大的导航能力:
// 提取深层嵌套属性
String author = response.jsonPath().getString("store.book[0].author");
// 跨层级搜索
List<String> allAuthors = response.jsonPath().getList("store..author");
// 条件过滤嵌套数组
List<Map<String, Object>> fictionBooks = response.jsonPath()
.getList("store.book.findAll { it.category == 'fiction' }");
高级验证技术
1. 复杂条件验证
使用Hamcrest匹配器进行复杂的条件验证:
// 验证数组包含特定元素
given().when().get("/books")
.then().body("store.book.author", hasItems("J. R. R. Tolkien", "Herman Melville"));
// 验证嵌套对象属性
given().when().get("/books")
.then().body("store.book[0].price", equalTo(8.95f));
// 验证数组大小
given().when().get("/books")
.then().body("store.book.size()", equalTo(4));
2. 类型安全的验证
结合Java 8的Lambda表达式进行类型安全的验证:
// 使用TypeRef处理泛型类型
TypeRef<List<Book>> bookListType = new TypeRef<List<Book>>() {};
List<Book> books = response.jsonPath().getObject("store.book", bookListType);
// 验证复杂对象结构
assertThat(books).extracting(Book::getCategory)
.contains("fiction", "reference");
实战示例:电商订单系统
假设我们有一个电商订单系统的API响应:
{
"orderId": "ORD-12345",
"customer": {
"name": "John Doe",
"email": "john@example.com",
"address": {
"street": "123 Main St",
"city": "New York",
"zipcode": "10001"
}
},
"items": [
{
"productId": "P001",
"name": "Laptop",
"quantity": 1,
"price": 999.99,
"specifications": {
"processor": "Intel i7",
"ram": "16GB",
"storage": "512GB SSD"
}
},
{
"productId": "P002",
"name": "Mouse",
"quantity": 2,
"price": 25.50
}
],
"totalAmount": 1050.99,
"status": "PROCESSING"
}
对应的验证代码:
// 提取订单信息
String orderId = response.jsonPath().getString("orderId");
List<Map<String, Object>> items = response.jsonPath().getList("items");
// 验证订单总额
assertThat(response.jsonPath().getDouble("totalAmount")).isEqualTo(1050.99);
// 验证商品数量
assertThat(response.jsonPath().getList("items")).hasSize(2);
// 验证笔记本电脑规格
Map<String, Object> laptopSpecs = response.jsonPath().getMap("items[0].specifications");
assertThat(laptopSpecs).containsEntry("processor", "Intel i7")
.containsEntry("ram", "16GB");
// 使用Groovy表达式验证高价商品
List<Map<String, Object>> expensiveItems = response.jsonPath()
.getList("items.findAll { it.price > 100 }");
assertThat(expensiveItems).hasSize(1)
.extracting("name").contains("Laptop");
性能优化技巧
处理大型复杂JSON时,可以考虑以下优化策略:
| 优化策略 | 说明 | 示例 |
|---|---|---|
| 延迟解析 | 只在需要时解析JSON | response.jsonPath().setRootPath("data") |
| 路径缓存 | 重复使用JsonPath实例 | JsonPath jsonPath = response.jsonPath() |
| 选择性验证 | 只验证必要的字段 | .body("items[0].name", equalTo("Laptop")) |
错误处理和调试
当处理复杂数据结构时,适当的错误处理至关重要:
try {
// 尝试提取可能不存在的路径
List<String> tags = response.jsonPath().getList("items[0].tags");
} catch (Exception e) {
// 处理路径不存在的情况
logger.warn("Tags field not found in response");
}
// 使用安全的方法检查路径是否存在
boolean hasTags = response.jsonPath().get("items[0].tags") != null;
最佳实践总结
- 使用类型安全的方法:优先使用
getList(path, Class<T>)和getObject(path, Class<T>)方法 - 合理使用Groovy表达式:对于复杂过滤逻辑,利用Groovy的表达能力
- 分层验证:先验证整体结构,再深入验证细节字段
- 性能考量:对于大型JSON,避免不必要的完整解析
- 错误恢复:为可能缺失的字段提供适当的错误处理机制
通过掌握这些复杂数据结构的提取和验证技术,您将能够有效地测试现代REST API的复杂响应,确保数据的一致性和正确性。
通过系统掌握JsonPath和XmlPath的强大功能,开发者能够高效处理各种复杂的数据结构,包括嵌套对象、数组、列表和映射等。文章详细阐述了响应体验证的最佳实践,包括状态码验证、内容类型检查、响应头验证以及使用Hamcrest匹配器进行复杂条件断言。这些技术不仅提高了API测试的效率和准确性,还增强了测试代码的可维护性和健壮性,为构建高质量的REST API测试体系奠定了坚实基础。
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00