Java XML 파싱

불냥·2024년 8월 12일
post-thumbnail

업무 중 Java에서 openApi를 통한 결과가 XML형태로 반환되어 해당 결과를 파싱하는 작업을 하였다.

openApi의 url은 https://www.nl.go.kr/NL/search/openApi/search.do? 형식으로 국립중앙도서관의 isbn 검색 API였고 신청한 키값과 isbn 코드를 이용하여 결과를 받아오기로 함.

우선 결과를 먼저 확인해보기 위해 해당 API를 호출을 해보았고,

<root>
    <paramData>
        <kwd></kwd>
        <category></category>
        <pageNum></pageNum>
        <pageSize></pageSize>
        <sort></sort>
        <total></total>
    </paramData>
    <result>
        <item>
            <title_info></title_info>
            <type_name></type_name>
            <place_info></place_info>
            <author_info></author_info>
            <pub_info></pub_info>
            <menu_name></menu_name>
            <media_name></media_name>
            <manage_name></manage_name>
            <pub_year_info></pub_year_info>
            <control_no></control_no>
            <doc_yn></doc_yn>
            <org_link></org_link>
            <id></id>
            <place_code></place_code>
            <type_code></type_code>
            <lic_yn></lic_yn>
            <lic_text></lic_text>
            <reg_date></reg_date>
            <image_url></image_url>
            <detail_link></detail_link>
            <isbn></isbn>
            <call_no></call_no>
            <kdc_code_1s></kdc_code_1s>
            <kdc_name_1s></kdc_name_1s>
            <class_no></class_no>
            <type_of_res></type_of_res>
        </item>
    </result>
</root>

이러한 형태의 xml 값이 들어오는 것을 확인하였다.

해당 xml 형태를 보았을 때 현재 개발 중인 DB의 컬럼 구조는 컬럼 구조가 camelCase로 되어있기 때문에 snake_case를 변경을 하는 방향으로 개발을 진행함.

아래는 해당 기준으로 개발한 코드이다.

// key 값은 따로 설정을 해주었고, isbn번호는 api 호출 시 받아오도록 설정
String urlString = "https://www.nl.go.kr/NL/search/openApi/search.do?"
+ "key=" + KJMS_BOOK_KEY
+ "&detailSearch=true&isbnOp=isbn&isbnCode=" + searchFilter.get("isbnNo");

StringBuilder urlBuilder = new StringBuilder(urlString);
// DocumentBuilderFactory 인스턴스를 생성하여 XML 파서를 위한 팩토리 클래스를 초기화
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

// DocumentBuilder 인스턴스를 생성하여 XML 파서를 초기화
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

// 주어진 URL을 통해 XML 문서를 파싱하고, null이 아닌지 확인한 후 Document 객체로 변환
Document document = Objects.requireNonNull(dBuilder.parse(urlBuilder.toString()));

// XML 문서의 루트 요소를 정규화 (정규화: XML 노드 간의 공백 제거 등 정리 작업)
document.getDocumentElement().normalize();

// XML 문서에서 "result" 태그를 가진 첫 번째 요소를 가져와서 rootNode로 할당
Node rootNode = document.getDocumentElement().getElementsByTagName("result").item(0);

// CommonUtil.getChildNodeArrays() 메서드를 사용하여 rootNode의 자식 노드를 가져와 리스트로 변환
List<Node> recordList = CommonUtil.getChildNodeArrays(rootNode.getChildNodes());

// recordList의 각 노드를 처리하여 맵 형태로 변환하고, 이를 dataList 리스트로 수집
List<Map<String, Object>> dataList = recordList.stream().map(node -> {

  // 현재 노드의 자식 노드를 가져와 리스트로 변환
  List<Node> itemList = CommonUtil.getChildNodeArrays(node.getChildNodes());

  // 데이터를 저장할 빈 맵 생성
  Map<String, Object> data = new HashMap<>();

  // 각 자식 노드를 처리
  itemList.forEach(colNode -> {
    // 자식 노드의 이름을 가져옴 (컬럼 이름)
    String colName = colNode.getNodeName();
    
    // 자식 노드의 내용을 가져옴 (컬럼 값)
    String colValue = colNode.getTextContent();

    // 컬럼 이름을 카멜 케이스로 변환하기 위한 StringBuilder 생성
    StringBuilder camelCase = new StringBuilder();
    
    // 다음 문자를 대문자로 변환할지 여부를 저장하는 플래그
    boolean toUppercase = false;

    // 컬럼 이름의 각 문자에 대해 반복
    for (char c : colName.toCharArray()) {
      if (c == '_') {
        // '_' 문자를 만나면 다음 문자를 대문자로 변환하도록 설정
        toUppercase = true;
      } else if (toUppercase) {
        // 플래그가 true이면, 현재 문자를 대문자로 변환하여 추가
        camelCase.append(Character.toUpperCase(c));
        toUppercase = false;
      } else {
        // 그렇지 않으면, 현재 문자를 그대로 추가
        camelCase.append(c);
      }
    }

    // 변환된 카멜 케이스 이름과 컬럼 값을 맵에 추가
    data.put(camelCase.toString(), colValue);
  });

  // 변환된 맵을 반환 (이 맵은 스트림에서 최종적으로 수집됨)
  return data;
}).collect(Collectors.toList()); // 스트림을 List<Map<String, Object>> 형태로 수집하여 dataList에 저장
profile
개인 공부 및 프로젝트 중 발생한 오류에 대한 정리를 위한 블로그입니다.

0개의 댓글