업무 중 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에 저장