返回列表 上一筆 下一筆

目前格式:HTML/XML

🧾 資料內容 (HTML/XML)

import org.w3c.dom.*;

import org.xml.sax.InputSource;

import javax.xml.parsers.*;

import java.io.StringReader;

import java.time.LocalDateTime;

import java.time.format.DateTimeFormatter;

import java.time.format.DateTimeParseException;

import java.util.*;



public class XmlTreeParser {



    private static final DateTimeFormatter FMT_PRIMARY = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");

    private static final DateTimeFormatter FMT_FALLBACK = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");



    /** 從字串解析,回傳 flat List<TreeNodeBean> */

    public static List<TreeNodeBean> parseFromString(String xml) throws Exception {

        if (xml == null || xml.isBlank()) return List.of();



        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();



        // 安全性:關閉外部實體與 DTD,避免 XXE(在內網系統很常見)

        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

        factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

        factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

        factory.setXIncludeAware(false);

        factory.setExpandEntityReferences(false);



        DocumentBuilder builder = factory.newDocumentBuilder();

        Document doc = builder.parse(new InputSource(new StringReader(xml)));

        doc.getDocumentElement().normalize();



        NodeList list = doc.getElementsByTagName("TreeSubdir");



        List<TreeNodeBean> result = new ArrayList<>();

        for (int i = 0; i < list.getLength(); i++) {

            Element e = (Element) list.item(i);



            String sftPath  = getTagValue(e, "sft_path");

            String iftSid   = getTagValue(e, "ift_sid");

            String sftName  = getTagValue(e, "sft_name");

            String iKBCount = getTagValue(e, "iKBCount");

            String dInit    = getTagValue(e, "dinit_time");

            String dModi    = getTagValue(e, "dmodi_time");

            String iftPrev  = getTagValue(e, "ift_prev");



            TreeNodeBean bean = new TreeNodeBean();

            bean.setId(iftSid);

            bean.setParentId(iftPrev);

            bean.setName(sftName);



            // 路徑:去頭尾反斜線,split 之後過濾空白

            bean.setPathRaw(sftPath);

            bean.setPathParts(splitPathSafe(sftPath));



            // KBCount:若無法轉數字就設 null(或改成 0)

            try {

                bean.setKbCount(iKBCount == null || iKBCount.isBlank() ? null : Integer.parseInt(iKBCount.trim()));

            } catch (NumberFormatException ex) {

                bean.setKbCount(null);

            }



            // 時間:允許空值;先用主格式,失敗再 fallback

            bean.setInitTime(parseDateTimeSafely(dInit));

            bean.setModiTime(parseDateTimeSafely(dModi));



            result.add(bean);

        }



        return result;

    }



    /** 單一標籤取值(忽略不存在) */

    private static String getTagValue(Element e, String tag) {

        NodeList nl = e.getElementsByTagName(tag);

        if (nl.getLength() > 0 && nl.item(0) != null) {

            String s = nl.item(0).getTextContent();

            return s == null ? "" : s.trim();

        }

        return "";

    }



    /** 解析 \3808\3811\3813\ → ["3808","3811","3813"](容錯) */

    private static List<String> splitPathSafe(String raw) {

        if (raw == null) return List.of();

        String s = raw.trim();

        if (s.startsWith("\\")) s = s.substring(1);

        if (s.endsWith("\\")) s = s.substring(0, s.length() - 1);

        if (s.isEmpty()) return List.of();



        String[] parts = s.split("\\\\"); // 兩個反斜線為 regex 裡的 '\'

        List<String> out = new ArrayList<>(parts.length);

        for (String p : parts) {

            if (p != null && !p.isBlank()) out.add(p.trim());

        }

        return out;

    }



    /** 安全解析時間:空字串→null;主格式失敗→fallback;都失敗→null */

    private static LocalDateTime parseDateTimeSafely(String s) {

        if (s == null || s.isBlank()) return null;

        String v = s.trim();

        try {

            return LocalDateTime.parse(v, FMT_PRIMARY);

        } catch (DateTimeParseException e) {

            try {

                return LocalDateTime.parse(v, FMT_FALLBACK);

            } catch (DateTimeParseException ignored) {

                return null;

            }

        }

    }



    // --- Demo ----

    public static void main(String[] args) throws Exception {

        String xml = """

            <root>

              <TreeSubdir>

                <sft_name>業務書櫃</sft_name>

                <ift_sid>3808</ift_sid>

                <ift_prev>0</ift_prev>

                <sft_path>\\</sft_path>

                <iKBCount>1</iKBCount>

                <dinit_time>2022/04/28 11:20:59</dinit_time>

                <dmodi_time>2024/01/03 17:19:03</dmodi_time>

              </TreeSubdir>

              <TreeSubdir>

                <sft_name>M#</sft_name>

                <ift_sid>3809</ift_sid>

                <ift_prev>3808</ift_prev>

                <sft_path>\\3808\\</sft_path>

                <iKBCount>4</iKBCount>

                <dinit_time>2022/04/28 11:20:59</dinit_time>

                <dmodi_time>2024/01/03 17:19:03</dmodi_time>

              </TreeSubdir>

            </root>

        """;



        List<TreeNodeBean> flat = parseFromString(xml);



        // 建樹 + 印樹(沿用你前面那組方法)

        List<TreeNodeBean> roots = TreeUtils.buildTree(flat);

        TreeUtils.printTree(roots, 0);



        // 檢核任一節點 path

        Map<String, TreeNodeBean> idx = TreeUtils.buildIndex(flat);

        for (TreeNodeBean n : flat) {

            boolean ok = TreeUtils.validateSftPath(n, idx);

            System.out.println(n.getId() + " path check = " + ok + " raw=" + n.getPathRaw() +

                               " -> " + TreeUtils.getPathIdsFromRoot(n, idx, false));

        }

    }

}

🔐 Base64 編碼內容

aW1wb3J0IG9yZy53M2MuZG9tLio7CgppbXBvcnQgb3JnLnhtbC5zYXguSW5wdXRTb3VyY2U7CgppbXBvcnQgamF2YXgueG1sLnBhcnNlcnMuKjsKCmltcG9ydCBqYXZhLmlvLlN0cmluZ1JlYWRlcjsKCmltcG9ydCBqYXZhLnRpbWUuTG9jYWxEYXRlVGltZTsKCmltcG9ydCBqYXZhLnRpbWUuZm9ybWF0LkRhdGVUaW1lRm9ybWF0dGVyOwoKaW1wb3J0IGphdmEudGltZS5mb3JtYXQuRGF0ZVRpbWVQYXJzZUV4Y2VwdGlvbjsKCmltcG9ydCBqYXZhLnV0aWwuKjsKCgoKcHVibGljIGNsYXNzIFhtbFRyZWVQYXJzZXIgewoKCgogICAgcHJpdmF0ZSBzdGF0aWMgZmluYWwgRGF0ZVRpbWVGb3JtYXR0ZXIgRk1UX1BSSU1BUlkgPSBEYXRlVGltZUZvcm1hdHRlci5vZlBhdHRlcm4oInl5eXkvTU0vZGQgSEg6bW06c3MiKTsKCiAgICBwcml2YXRlIHN0YXRpYyBmaW5hbCBEYXRlVGltZUZvcm1hdHRlciBGTVRfRkFMTEJBQ0sgPSBEYXRlVGltZUZvcm1hdHRlci5vZlBhdHRlcm4oInl5eXktTU0tZGQgSEg6bW06c3MiKTsKCgoKICAgIC8qKiDlvp7lrZfkuLLop6PmnpDvvIzlm57lgrMgZmxhdCBMaXN0PFRyZWVOb2RlQmVhbj4gKi8KCiAgICBwdWJsaWMgc3RhdGljIExpc3Q8VHJlZU5vZGVCZWFuPiBwYXJzZUZyb21TdHJpbmcoU3RyaW5nIHhtbCkgdGhyb3dzIEV4Y2VwdGlvbiB7CgogICAgICAgIGlmICh4bWwgPT0gbnVsbCB8fCB4bWwuaXNCbGFuaygpKSByZXR1cm4gTGlzdC5vZigpOwoKCgogICAgICAgIERvY3VtZW50QnVpbGRlckZhY3RvcnkgZmFjdG9yeSA9IERvY3VtZW50QnVpbGRlckZhY3RvcnkubmV3SW5zdGFuY2UoKTsKCgoKICAgICAgICAvLyDlronlhajmgKfvvJrpl5zplonlpJbpg6jlr6bpq5ToiIcgRFRE77yM6YG/5YWNIFhYRe+8iOWcqOWFp+e2suezu+e1seW+iOW4uOimi++8iQoKICAgICAgICBmYWN0b3J5LnNldEZlYXR1cmUoImh0dHA6Ly9hcGFjaGUub3JnL3htbC9mZWF0dXJlcy9kaXNhbGxvdy1kb2N0eXBlLWRlY2wiLCB0cnVlKTsKCiAgICAgICAgZmFjdG9yeS5zZXRGZWF0dXJlKCJodHRwOi8veG1sLm9yZy9zYXgvZmVhdHVyZXMvZXh0ZXJuYWwtZ2VuZXJhbC1lbnRpdGllcyIsIGZhbHNlKTsKCiAgICAgICAgZmFjdG9yeS5zZXRGZWF0dXJlKCJodHRwOi8veG1sLm9yZy9zYXgvZmVhdHVyZXMvZXh0ZXJuYWwtcGFyYW1ldGVyLWVudGl0aWVzIiwgZmFsc2UpOwoKICAgICAgICBmYWN0b3J5LnNldFhJbmNsdWRlQXdhcmUoZmFsc2UpOwoKICAgICAgICBmYWN0b3J5LnNldEV4cGFuZEVudGl0eVJlZmVyZW5jZXMoZmFsc2UpOwoKCgogICAgICAgIERvY3VtZW50QnVpbGRlciBidWlsZGVyID0gZmFjdG9yeS5uZXdEb2N1bWVudEJ1aWxkZXIoKTsKCiAgICAgICAgRG9jdW1lbnQgZG9jID0gYnVpbGRlci5wYXJzZShuZXcgSW5wdXRTb3VyY2UobmV3IFN0cmluZ1JlYWRlcih4bWwpKSk7CgogICAgICAgIGRvYy5nZXREb2N1bWVudEVsZW1lbnQoKS5ub3JtYWxpemUoKTsKCgoKICAgICAgICBOb2RlTGlzdCBsaXN0ID0gZG9jLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJUcmVlU3ViZGlyIik7CgoKCiAgICAgICAgTGlzdDxUcmVlTm9kZUJlYW4+IHJlc3VsdCA9IG5ldyBBcnJheUxpc3Q8PigpOwoKICAgICAgICBmb3IgKGludCBpID0gMDsgaSA8IGxpc3QuZ2V0TGVuZ3RoKCk7IGkrKykgewoKICAgICAgICAgICAgRWxlbWVudCBlID0gKEVsZW1lbnQpIGxpc3QuaXRlbShpKTsKCgoKICAgICAgICAgICAgU3RyaW5nIHNmdFBhdGggID0gZ2V0VGFnVmFsdWUoZSwgInNmdF9wYXRoIik7CgogICAgICAgICAgICBTdHJpbmcgaWZ0U2lkICAgPSBnZXRUYWdWYWx1ZShlLCAiaWZ0X3NpZCIpOwoKICAgICAgICAgICAgU3RyaW5nIHNmdE5hbWUgID0gZ2V0VGFnVmFsdWUoZSwgInNmdF9uYW1lIik7CgogICAgICAgICAgICBTdHJpbmcgaUtCQ291bnQgPSBnZXRUYWdWYWx1ZShlLCAiaUtCQ291bnQiKTsKCiAgICAgICAgICAgIFN0cmluZyBkSW5pdCAgICA9IGdldFRhZ1ZhbHVlKGUsICJkaW5pdF90aW1lIik7CgogICAgICAgICAgICBTdHJpbmcgZE1vZGkgICAgPSBnZXRUYWdWYWx1ZShlLCAiZG1vZGlfdGltZSIpOwoKICAgICAgICAgICAgU3RyaW5nIGlmdFByZXYgID0gZ2V0VGFnVmFsdWUoZSwgImlmdF9wcmV2Iik7CgoKCiAgICAgICAgICAgIFRyZWVOb2RlQmVhbiBiZWFuID0gbmV3IFRyZWVOb2RlQmVhbigpOwoKICAgICAgICAgICAgYmVhbi5zZXRJZChpZnRTaWQpOwoKICAgICAgICAgICAgYmVhbi5zZXRQYXJlbnRJZChpZnRQcmV2KTsKCiAgICAgICAgICAgIGJlYW4uc2V0TmFtZShzZnROYW1lKTsKCgoKICAgICAgICAgICAgLy8g6Lev5b6R77ya5Y676aCt5bC+5Y+N5pac57ea77yMc3BsaXQg5LmL5b6M6YGO5r++56m655m9CgogICAgICAgICAgICBiZWFuLnNldFBhdGhSYXcoc2Z0UGF0aCk7CgogICAgICAgICAgICBiZWFuLnNldFBhdGhQYXJ0cyhzcGxpdFBhdGhTYWZlKHNmdFBhdGgpKTsKCgoKICAgICAgICAgICAgLy8gS0JDb3VudO+8muiLpeeEoeazlei9ieaVuOWtl+WwseiorSBudWxs77yI5oiW5pS55oiQIDDvvIkKCiAgICAgICAgICAgIHRyeSB7CgogICAgICAgICAgICAgICAgYmVhbi5zZXRLYkNvdW50KGlLQkNvdW50ID09IG51bGwgfHwgaUtCQ291bnQuaXNCbGFuaygpID8gbnVsbCA6IEludGVnZXIucGFyc2VJbnQoaUtCQ291bnQudHJpbSgpKSk7CgogICAgICAgICAgICB9IGNhdGNoIChOdW1iZXJGb3JtYXRFeGNlcHRpb24gZXgpIHsKCiAgICAgICAgICAgICAgICBiZWFuLnNldEtiQ291bnQobnVsbCk7CgogICAgICAgICAgICB9CgoKCiAgICAgICAgICAgIC8vIOaZgumWk++8muWFgeioseepuuWAvO+8m+WFiOeUqOS4u+agvOW8j++8jOWkseaVl+WGjSBmYWxsYmFjawoKICAgICAgICAgICAgYmVhbi5zZXRJbml0VGltZShwYXJzZURhdGVUaW1lU2FmZWx5KGRJbml0KSk7CgogICAgICAgICAgICBiZWFuLnNldE1vZGlUaW1lKHBhcnNlRGF0ZVRpbWVTYWZlbHkoZE1vZGkpKTsKCgoKICAgICAgICAgICAgcmVzdWx0LmFkZChiZWFuKTsKCiAgICAgICAgfQoKCgogICAgICAgIHJldHVybiByZXN1bHQ7CgogICAgfQoKCgogICAgLyoqIOWWruS4gOaomeexpOWPluWAvO+8iOW/veeVpeS4jeWtmOWcqO+8iSAqLwoKICAgIHByaXZhdGUgc3RhdGljIFN0cmluZyBnZXRUYWdWYWx1ZShFbGVtZW50IGUsIFN0cmluZyB0YWcpIHsKCiAgICAgICAgTm9kZUxpc3QgbmwgPSBlLmdldEVsZW1lbnRzQnlUYWdOYW1lKHRhZyk7CgogICAgICAgIGlmIChubC5nZXRMZW5ndGgoKSA+IDAgJiYgbmwuaXRlbSgwKSAhPSBudWxsKSB7CgogICAgICAgICAgICBTdHJpbmcgcyA9IG5sLml0ZW0oMCkuZ2V0VGV4dENvbnRlbnQoKTsKCiAgICAgICAgICAgIHJldHVybiBzID09IG51bGwgPyAiIiA6IHMudHJpbSgpOwoKICAgICAgICB9CgogICAgICAgIHJldHVybiAiIjsKCiAgICB9CgoKCiAgICAvKiog6Kej5p6QIFwzODA4XDM4MTFcMzgxM1wg4oaSIFsiMzgwOCIsIjM4MTEiLCIzODEzIl3vvIjlrrnpjK/vvIkgKi8KCiAgICBwcml2YXRlIHN0YXRpYyBMaXN0PFN0cmluZz4gc3BsaXRQYXRoU2FmZShTdHJpbmcgcmF3KSB7CgogICAgICAgIGlmIChyYXcgPT0gbnVsbCkgcmV0dXJuIExpc3Qub2YoKTsKCiAgICAgICAgU3RyaW5nIHMgPSByYXcudHJpbSgpOwoKICAgICAgICBpZiAocy5zdGFydHNXaXRoKCJcXCIpKSBzID0gcy5zdWJzdHJpbmcoMSk7CgogICAgICAgIGlmIChzLmVuZHNXaXRoKCJcXCIpKSBzID0gcy5zdWJzdHJpbmcoMCwgcy5sZW5ndGgoKSAtIDEpOwoKICAgICAgICBpZiAocy5pc0VtcHR5KCkpIHJldHVybiBMaXN0Lm9mKCk7CgoKCiAgICAgICAgU3RyaW5nW10gcGFydHMgPSBzLnNwbGl0KCJcXFxcIik7IC8vIOWFqeWAi+WPjeaWnOe3mueCuiByZWdleCDoo6HnmoQgJ1wnCgogICAgICAgIExpc3Q8U3RyaW5nPiBvdXQgPSBuZXcgQXJyYXlMaXN0PD4ocGFydHMubGVuZ3RoKTsKCiAgICAgICAgZm9yIChTdHJpbmcgcCA6IHBhcnRzKSB7CgogICAgICAgICAgICBpZiAocCAhPSBudWxsICYmICFwLmlzQmxhbmsoKSkgb3V0LmFkZChwLnRyaW0oKSk7CgogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIG91dDsKCiAgICB9CgoKCiAgICAvKiog5a6J5YWo6Kej5p6Q5pmC6ZaT77ya56m65a2X5Liy4oaSbnVsbO+8m+S4u+agvOW8j+WkseaVl+KGkmZhbGxiYWNr77yb6YO95aSx5pWX4oaSbnVsbCAqLwoKICAgIHByaXZhdGUgc3RhdGljIExvY2FsRGF0ZVRpbWUgcGFyc2VEYXRlVGltZVNhZmVseShTdHJpbmcgcykgewoKICAgICAgICBpZiAocyA9PSBudWxsIHx8IHMuaXNCbGFuaygpKSByZXR1cm4gbnVsbDsKCiAgICAgICAgU3RyaW5nIHYgPSBzLnRyaW0oKTsKCiAgICAgICAgdHJ5IHsKCiAgICAgICAgICAgIHJldHVybiBMb2NhbERhdGVUaW1lLnBhcnNlKHYsIEZNVF9QUklNQVJZKTsKCiAgICAgICAgfSBjYXRjaCAoRGF0ZVRpbWVQYXJzZUV4Y2VwdGlvbiBlKSB7CgogICAgICAgICAgICB0cnkgewoKICAgICAgICAgICAgICAgIHJldHVybiBMb2NhbERhdGVUaW1lLnBhcnNlKHYsIEZNVF9GQUxMQkFDSyk7CgogICAgICAgICAgICB9IGNhdGNoIChEYXRlVGltZVBhcnNlRXhjZXB0aW9uIGlnbm9yZWQpIHsKCiAgICAgICAgICAgICAgICByZXR1cm4gbnVsbDsKCiAgICAgICAgICAgIH0KCiAgICAgICAgfQoKICAgIH0KCgoKICAgIC8vIC0tLSBEZW1vIC0tLS0KCiAgICBwdWJsaWMgc3RhdGljIHZvaWQgbWFpbihTdHJpbmdbXSBhcmdzKSB0aHJvd3MgRXhjZXB0aW9uIHsKCiAgICAgICAgU3RyaW5nIHhtbCA9ICIiIgoKICAgICAgICAgICAgPHJvb3Q+CgogICAgICAgICAgICAgIDxUcmVlU3ViZGlyPgoKICAgICAgICAgICAgICAgIDxzZnRfbmFtZT7mpa3li5nmm7jmq4M8L3NmdF9uYW1lPgoKICAgICAgICAgICAgICAgIDxpZnRfc2lkPjM4MDg8L2lmdF9zaWQ+CgogICAgICAgICAgICAgICAgPGlmdF9wcmV2PjA8L2lmdF9wcmV2PgoKICAgICAgICAgICAgICAgIDxzZnRfcGF0aD5cXDwvc2Z0X3BhdGg+CgogICAgICAgICAgICAgICAgPGlLQkNvdW50PjE8L2lLQkNvdW50PgoKICAgICAgICAgICAgICAgIDxkaW5pdF90aW1lPjIwMjIvMDQvMjggMTE6MjA6NTk8L2Rpbml0X3RpbWU+CgogICAgICAgICAgICAgICAgPGRtb2RpX3RpbWU+MjAyNC8wMS8wMyAxNzoxOTowMzwvZG1vZGlfdGltZT4KCiAgICAgICAgICAgICAgPC9UcmVlU3ViZGlyPgoKICAgICAgICAgICAgICA8VHJlZVN1YmRpcj4KCiAgICAgICAgICAgICAgICA8c2Z0X25hbWU+TSM8L3NmdF9uYW1lPgoKICAgICAgICAgICAgICAgIDxpZnRfc2lkPjM4MDk8L2lmdF9zaWQ+CgogICAgICAgICAgICAgICAgPGlmdF9wcmV2PjM4MDg8L2lmdF9wcmV2PgoKICAgICAgICAgICAgICAgIDxzZnRfcGF0aD5cXDM4MDhcXDwvc2Z0X3BhdGg+CgogICAgICAgICAgICAgICAgPGlLQkNvdW50PjQ8L2lLQkNvdW50PgoKICAgICAgICAgICAgICAgIDxkaW5pdF90aW1lPjIwMjIvMDQvMjggMTE6MjA6NTk8L2Rpbml0X3RpbWU+CgogICAgICAgICAgICAgICAgPGRtb2RpX3RpbWU+MjAyNC8wMS8wMyAxNzoxOTowMzwvZG1vZGlfdGltZT4KCiAgICAgICAgICAgICAgPC9UcmVlU3ViZGlyPgoKICAgICAgICAgICAgPC9yb290PgoKICAgICAgICAiIiI7CgoKCiAgICAgICAgTGlzdDxUcmVlTm9kZUJlYW4+IGZsYXQgPSBwYXJzZUZyb21TdHJpbmcoeG1sKTsKCgoKICAgICAgICAvLyDlu7rmqLkgKyDljbDmqLnvvIjmsr/nlKjkvaDliY3pnaLpgqPntYTmlrnms5XvvIkKCiAgICAgICAgTGlzdDxUcmVlTm9kZUJlYW4+IHJvb3RzID0gVHJlZVV0aWxzLmJ1aWxkVHJlZShmbGF0KTsKCiAgICAgICAgVHJlZVV0aWxzLnByaW50VHJlZShyb290cywgMCk7CgoKCiAgICAgICAgLy8g5qqi5qC45Lu75LiA56+A6bueIHBhdGgKCiAgICAgICAgTWFwPFN0cmluZywgVHJlZU5vZGVCZWFuPiBpZHggPSBUcmVlVXRpbHMuYnVpbGRJbmRleChmbGF0KTsKCiAgICAgICAgZm9yIChUcmVlTm9kZUJlYW4gbiA6IGZsYXQpIHsKCiAgICAgICAgICAgIGJvb2xlYW4gb2sgPSBUcmVlVXRpbHMudmFsaWRhdGVTZnRQYXRoKG4sIGlkeCk7CgogICAgICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4obi5nZXRJZCgpICsgIiBwYXRoIGNoZWNrID0gIiArIG9rICsgIiByYXc9IiArIG4uZ2V0UGF0aFJhdygpICsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIC0+ICIgKyBUcmVlVXRpbHMuZ2V0UGF0aElkc0Zyb21Sb290KG4sIGlkeCwgZmFsc2UpKTsKCiAgICAgICAgfQoKICAgIH0KCn0K
返回列表 上一筆 下一筆