From 71fbf90ad7f079d8935191fd1503769322273e81 Mon Sep 17 00:00:00 2001 From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:21:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E5=BC=95=E6=93=8E?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20JSON=20=E5=86=85=E5=AE=B9=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=B1=95=E7=A4=BA=E7=BB=93=E6=9E=84=EF=BC=8C=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E8=A7=A3=E6=9E=90=E4=BC=A0=E5=85=A5=20contentType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../par/api/controller/AdapterController.java | 8 +- .../com/par/core/adapter/AdapterEngine.java | 86 +++++++++++++++++-- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/par-api/src/main/java/com/par/api/controller/AdapterController.java b/par-api/src/main/java/com/par/api/controller/AdapterController.java index 310d655..0a7ed18 100644 --- a/par-api/src/main/java/com/par/api/controller/AdapterController.java +++ b/par-api/src/main/java/com/par/api/controller/AdapterController.java @@ -133,11 +133,11 @@ public class AdapterController { item.put("invalidCount", allResults.stream().filter(r -> url.equals(r.getUrl()) && Boolean.FALSE.equals(r.getIsValid())).count()); item.put("uncheckedCount", allResults.stream().filter(r -> url.equals(r.getUrl()) && r.getIsValid() == null).count()); - // 解析 HTML + // 解析内容 try { - String html = storageService.read(ar.getStoragePath()); - if (html != null) { - Map parsed = adapterEngine.extract(adapterConfig, html, url); + String content = storageService.read(ar.getStoragePath()); + if (content != null) { + Map parsed = adapterEngine.extract(adapterConfig, content, ar.getContentType(), url); item.put("parsed", parsed); } } catch (Exception e) { diff --git a/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java b/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java index 3e86518..b333c9d 100644 --- a/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java +++ b/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java @@ -23,29 +23,103 @@ public class AdapterEngine { private static final ObjectMapper objectMapper = new ObjectMapper(); /** - * 解析单个 HTML 页面,匹配配置中的页面规则并提取数据 - * @return { pageName, pageUrl, extracted: [{field: value, ...}, ...] } + * 解析页面内容(兼容 HTML 和 JSON) */ - public Map extract(AdapterConfig config, String html, String accessUrl) { + public Map extract(AdapterConfig config, String content, String contentType, String accessUrl) { + if ("json".equalsIgnoreCase(contentType)) { + return extractJson(config, content, accessUrl); + } + return extractHtml(config, content, accessUrl); + } + + /** 保留旧签名兼容单条解析 */ + public Map extract(AdapterConfig config, String content, String accessUrl) { + return extract(config, content, "html", accessUrl); + } + + /** + * 解析 JSON 内容:直接展示 JSON 结构 + */ + private Map extractJson(AdapterConfig config, String jsonStr, String accessUrl) { + Map result = new LinkedHashMap<>(); + try { + Object parsed = objectMapper.readValue(jsonStr, Object.class); + result.put("contentType", "json"); + result.put("matched", true); + result.put("pageName", "JSON响应"); + + if (parsed instanceof Map) { + @SuppressWarnings("unchecked") + Map map = (Map) parsed; + result.put("message", "JSON 对象,共 " + map.size() + " 个顶层字段"); + // 展示顶层字段 + List> rows = new ArrayList<>(); + Map row = new LinkedHashMap<>(); + for (String key : map.keySet()) { + Object val = map.get(key); + row.put(key, val instanceof String ? val : objectMapper.writeValueAsString(val)); + } + rows.add(row); + result.put("extracted", rows); + result.put("rowCount", 1); + result.put("fieldCount", map.size()); + } else if (parsed instanceof List) { + @SuppressWarnings("unchecked") + List list = (List) parsed; + result.put("message", "JSON 数组,共 " + list.size() + " 条记录"); + List> rows = new ArrayList<>(); + int max = Math.min(list.size(), 20); + for (int i = 0; i < max; i++) { + Object item = list.get(i); + Map row = new LinkedHashMap<>(); + if (item instanceof Map) { + @SuppressWarnings("unchecked") + Map m = (Map) item; + for (String key : m.keySet()) { + Object val = m.get(key); + row.put(key, val instanceof String ? val : objectMapper.writeValueAsString(val)); + } + } else { + row.put("value", item); + } + rows.add(row); + } + result.put("extracted", rows); + result.put("rowCount", list.size()); + result.put("fieldCount", rows.isEmpty() ? 0 : rows.get(0).size()); + } + } catch (Exception e) { + result.put("contentType", "json"); + result.put("matched", false); + result.put("message", "JSON 解析失败: " + e.getMessage()); + } + return result; + } + + /** + * 解析 HTML 内容:Jsoup + CSS 选择器 + */ + private Map extractHtml(AdapterConfig config, String html, String accessUrl) { Map result = new LinkedHashMap<>(); Document doc = Jsoup.parse(html); - // 匹配页面 Map.Entry matched = matchPage(config.getPages(), accessUrl); if (matched == null) { result.put("matched", false); + result.put("contentType", "html"); result.put("message", "未匹配到页面规则"); - result.put("availablePages", config.getPages().keySet()); + result.put("availablePages", new ArrayList<>(config.getPages() != null ? config.getPages().keySet() : Set.of())); + result.put("accessUrl", accessUrl); return result; } String pageName = matched.getKey(); PageConfig page = matched.getValue(); result.put("matched", true); + result.put("contentType", "html"); result.put("pageName", pageName); result.put("pageUrl", accessUrl); - // 提取数据 List> extracted = extractRows(doc, page); result.put("extracted", extracted); result.put("rowCount", extracted.size());