공부방
XML 파싱 본문
fast-xml-parser
- JavaScript로 작성된 빠르고 가벼운 XML 파싱 라이브러리
- XML 데이터를 JavaScript 객체로 변환하거나 JavaScript 객체를 XML 문자열로 변환할 수 있다.
- 특징
- 빠른 파싱 : 이름처럼 매우 빠르게 XML을 파싱할 수 있으며, 성능이 중요한 환경에서 유용
- 경량 : 종속성이 없고 가볍기 때문에 브라우저나 Node.js 환경 모두에서 사용하기 적합
- 옵션 설정 가능 : XML을 파싱할 때 다양한 옵션을 설정할 수 있다. 예를 들어, XML 속성 파싱, 태그 및 속성 이름 변환, 텍스트 및 숫자 데이터 유형 처리 등에 대한 세부 제어가 가능
- JSON 변환 : 파싱된 XML 데이터를 JSON 형식으로 변환할 수 있어, JSON API와의 상호 운용성이 좋음
- 유효성 검사 : XML 문자열에 대한 유효성 검사를 수행하여 올바른 XML 형식인지 확인할 수 있다.
import { XMLParser } from 'fast-xml-parser';
- XMLParser는 fast-xml-parser 라이브러리에서 XML 데이터를 파싱하는 도구
- 이 라이브러리를 사용하여 XML을 JavaScript 객체로 변환 가능
export const parseXML = (xmlData) => {
...
});
- parseXML이라는 함수를 만들어 다른 파일에서도 사용할 수 있도록 export
- xmlData는 이 함수가 호출될 때 전달받는 파라미터(입력값).
- XML 데이터가 문자열 형태로 들어오게 된다.
- 이 문자열에는 XML의 모든 태그, 속성, 값들을 포함한다.
<workout_file>
<name>베이스 훈련</name>
<workout>
<Warmup Duration="240" PowerLow="0.32" PowerHigh="0.60"/>
</workout>
</workout_file>
- 예를 들어 이런 XML이 있다면
- 이 XML 데이터를 문자열로 읽어온 것이 xmlData이다.
- JavaScript에서는 이 XML 데이터가 하나의 큰 문자열로 처리된다.
- "<workout_file><name>베이스 훈련</name><workout><Warmup Duration=\"240\" PowerLow=\"0.32\" PowerHigh=\"0.60\"/></workout></workout_file>"
- 이런 식으로
- 이 문자열은 현재는 단순한 텍스트이기 때문에, 그 안에 담긴 정보를 프로그램이 사용하려면 파싱하여 JavaScript 객체로 변환해야 한다.
export const parseXML = (xmlData) => {
const parser = new XMLParser({
ignoreAttributes: false, // 속성을 파싱할 수 있도록 설정
attributeNamePrefix: "", // 속성 앞에 붙는 @_를 제거
});
- new XMLParser({...})는 XMLParser 클래스의 인스턴스를 생성
- 이 인스턴스는 XML 데이터를 파싱하는 데 사용된다.
- ignoreAttributes : XML 파일에는 태그에 다양한 속성들이 포함되어 있을 수 있기 때문에, XML 태그에 포함된 속성들을 파싱할지 여부를 설정해야한다.
<Warmup Duration="240" PowerLow="0.32" PowerHigh="0.60"/>
- 예를 들어 이러한 XML 데이터가 있다고 하면.
- 이 XML 태그에는 Duration, PowerLow, PowerHigh와 같은 속성들이 있다.
- 이 속성들은 태그의 주요 정보로, 이를 파싱해서 사용하려면 속성까지 객체로 변환해야 한다.
만약 ignoreAttributes: false가 없다면(ignoreAttributes 기본값은 true다)
- 속성을 무시하고 태그 이름만 파싱하기 때문에, 속성에 대한 정보가 객체로 변환된지 않는다.
- 파싱 결과는 다음과 같이 속성을 포함하지 않고 빈 객체로 나타날 수 있다.
{
Warmup: {}
}
ignoreAttributes: fale를 설정한 경우
- 태그의 속성들도 함께 파싱하여 객체로 변환
- 파싱 결과는 다음과 같이 속성을 포함한 객체로 변환
{
Warmup: {
Duration: "240",
PowerLow: "0.32",
PowerHigh: "0.60"
}
}
- attributeNamePrefix : fase-xml-parser는 기본적으로 속성 이름 앞에 @_를 붙이는데, 이 옵션을 빈 문자열로 설정하여 @_를 제거하고 속성 이름을 그대로 사용할 수 있도록 설정
const jsonObj = parser.parse(xmlData);
- parser.pare(xmlData)를 호출하여 XML 문자열을 JavaScript 객체로 변환
- 결과는 jsonObj에 저장
- 파싱된 데이터는 XML의 계층 구조를 그대로 유지하는 JavaScript 객체가 된다.
- 예를 들어, <work_file> 태그의 하위에 있는 내용들은 jsonObj.workout_file로 접근할 수 있게 된다.
const workout = jsonObj.workout_file.workout;
let stages = [];
- jsonObj.workout_file.workout는 파싱된 XML 객체 중 <workout_file> 태그 안에 있는 <workout> 태그의 내용을 가져옴.
<workout_file>
<name>베이스 훈련</name>
<workout>
<Warmup Duration="240" PowerLow="0.32" PowerHigh="0.60"/>
<SteadyState Duration="60" Power="0.60"/>
</workout>
</workout_file>
- 이러한 XML 데이터가 있다고 하면
- 파싱된 객체는 이런 구조를 가지게 된다.
{
workout_file: {
name: "베이스 훈련",
workout: {
Warmup: { Duration: "240", PowerLow: "0.32", PowerHigh: "0.60" },
SteadyState: { Duration: "60", Power: "0.60" }
}
}
}
- stages 배열은 각 운동 단계를 저장하기 위해 만들어진다.
- 나중에 for 루프를 통해 <workout> 내부에 있는 모든 운동 단계의 정보를 배열에 하나씩 추가할 것임
for (const key in workout) {
// 내용
}
- for...in 루프를 사용하여 workout 객체의 모든 키를 순회
- key는 workout 객체 안에 있는 각각의 속성 이름을 의미한다.
- 예를 들어 Warmup, SteadyState 등
- 이 루프를 통해 각 워크 아웃 단계에 대한 정보를 하나씩 처리
if (Array.isArray(workout[key])) {
// 각 단계가 배열인 경우
workout[key].forEach((stage) => {
stages.push({
name: key,
...stage // 모든 속성 추가
});
});
}
- Array.isArray(workout[key])는 현재 단계(workout[key])가 배열 형태인지 확인
else if (typeof workout[key] === 'object') {
// 단일 객체로 존재하는 경우
stages.push({
name: key,
...workout[key] // 모든 속성 추가
});
}
- workout[key]가 배열이 아닌 단일 객체인 경우에 대한 처리
배열 객체와 단일 객체를 나누는 이유?
<workout>
<SteadyState Duration="60" Power="0.6"/>
<SteadyState Duration="60" Power="0.7"/>
</workout>
- XML에서는 같은 이름의 태그가 여러번 나타날 수 있다.
- 이 경우, fast-xml-parser는 같은 이름의 태그가 여러번 등장하면 이를 배열로 파싱하게 된다.
- 반면에, 해당 태그가 XML에서 한 번만 나타나는 경우, 이를 단일 객체로 파싱하게 된다.
fase-xml-parser의 파싱 방식
여러번 나타나는 태그
{
SteadyState: [
{ Duration: "60", Power: "0.6" },
{ Duration: "60", Power: "0.7" }
]
}
- 같은 태그가 여러번 나타나는 경우, 파싱 결과는 위와 같은 배열 형태가 된다.
한 번 나타나는 태그
{
Warmup: { Duration: "240", PowerLow: "0.32", PowerHigh: "0.60" }
}
- 파싱 결과는 위와 같이 단일 객체 형태가 된다.
따라서 배열인지 아닌지를 나누는 이유
- 태그가 배열일 때와 객체일 때 각각 다르게 처리하지 않으면, 파싱 결과에 따라 코드를 동적으로 작성할 수 없다.
- 예를 들어 Array.isArray를 통해 배열 여부를 확인하지 않고 동일한 방식으로 처리하려고 하면, 데이터 구조에 따라 코드가 제대로 동작하지 않을 수 있다.
- 만약 모든 데이터를 배열로 가정하고 forEach나 map 같은 배열 메서드를 사용하려고 한다면, 태그가 한번만 나타날 때 단일 객체로 파싱된 경우 에러가 발생하게 된다.
- 유연하고 에러 없는 코드를 만들기 위한 목적
for (const key in workout) {
if (Array.isArray(workout[key])) {
// 각 단계가 배열인 경우
workout[key].forEach((stage) => {
stages.push({
name: key,
...stage // 모든 속성 추가
});
});
} else if (typeof workout[key] === 'object') {
// 단일 객체로 존재하는 경우
stages.push({
name: key,
...workout[key] // 모든 속성 추가
});
}
}
<workout>
<Warmup Duration="240" PowerLow="0.32" PowerHigh="0.60"/>
<SteadyState Duration="60" Power="0.6"/>
<SteadyState Duration="60" Power="0.7"/>
</workout>
- 따라서 이러한 XML 데이터가 들어온다면
- for...in 루프가 처음 만나는 키는 Warmup이고, 이는 단일 객체.
- 따라서 else if 부분이 실행되어 stages에 이 객체를 추가
- 두번째 키는 SteadyState이며, 이는 배열
- 따라서 if 부분이 실행되고, 배열의 각 요소를 순회하며 stages에 추가
- 밑은 파싱된 jsonObj 구조
{
workout_file: {
workout: {
Warmup: { Duration: "240", PowerLow: "0.32", PowerHigh: "0.60" },
SteadyState: [
{ Duration: "60", Power: "0.6" },
{ Duration: "60", Power: "0.7" }
]
}
}
}
[
{ name: "Warmup", Duration: "240", PowerLow: "0.32", PowerHigh: "0.60" },
{ name: "SteadyState", Duration: "60", Power: "0.6" },
{ name: "SteadyState", Duration: "60", Power: "0.7" }
]
결과적으로 stages 배열은 이와 같이 구성된다.
const workoutDetails = {
name: jsonObj.workout_file.name,
description: jsonObj.workout_file.description,
stages: stages
};
return workoutDetails;
};
- 워크아웃 정보 객체 생성 : 파싱된 정보를 workoutDetails라는 객체에 정리
- name : <name> 태그의 값을 저장
- description : <description> 태그의 값을 저장
- stages : 앞서 구성한 운동 단계들을 담고 있는 배열을 저장
- 반환 : 최종적으로 workoutDetails 객체를 반환.
- 이 객체는 다른 컴포넌트에서 워크아웃 정보를 시각화하거나 표시하는데 사용될 수 있다.
최종 코드
// XML 파일을 JavaScript 객체로 변환하여 필요한 데이터를 추출
import { XMLParser } from "fast-xml-parser";
// XMLData: XML 문자열
export const parseXML = (xmlData) => {
// XMLParser를 이용하여 XML을 JavaScript 객체로 변환
const parser = new XMLParser({
ignoreAttributes: false, // 속성을 파싱할 수 있도록 설정
attributeNamePrefix: "", // 속성 앞에 붙는 @_를 제거
});
// JavaScript 객체로 변환한 값을 jsonObj에 저장
const jsonObj = parser.parse(xmlData);
// 워크아웃 단계 추출
const workout = jsonObj.workout_file.workout;
let stages = [];
// 단계별로 파싱
for (const key in workout) {
// 파싱된 결과가 배열 객체와 단일 객체로 나눠 배열에 넣어준다
if (Array.isArray(workout[key])) {
// 각 단계가 배열인 경우
workout[key].forEach((stage) => {
stages.push({
name: key,
...stage, // 모든 속성 추가
});
});
} else if (typeof workout[key] === "object") {
// 단일 객체로 존재하는 경우
stages.push({
name: key,
...workout[key], // 모든 속성 추가
});
}
}
// 콘솔에 파싱된 데이터 출력
console.log("Parsed stages:", stages);
// 파싱된 정보들을 객체에 저장
const workoutDetails = {
name: jsonObj.workout_file.name,
description: jsonObj.workout_file.description,
stages: stages,
};
return workoutDetails;
};
'React' 카테고리의 다른 글
React Router-셋팅과 기본 라우팅 (0) | 2024.10.22 |
---|---|
차트 라이브러리-CanvasJsChart (3) | 2024.10.08 |
Card 컴포넌트 만들고 props, map (0) | 2024.08.17 |
import / export (0) | 2024.08.04 |
이미지 넣는 법 & public 폴더 이용하기 (0) | 2024.07.28 |