XML을 MongoDB로 데이터를 내보내야 할 많은 상황이 있을 수 있습니다.
MongoDB에서 사용되는 XML 및 JSON(B) 형식이 많은 공통점을 가지고 있지만, 서로 교환할 수 없는 몇 가지 차이점이 있습니다.
따라서 XML에서 MongoDB로 데이터를 내보내는 작업에 앞서 다음을 수행해야 합니다:
- 자체 XML 구문 분석 스크립트 작성;
- ETL 도구 사용.
현대적인 언어 모델은 Python과 같은 언어로 구문 분석 스크립트를 잘 작성할 수 있지만, 이러한 스크립트는 통일되지 않을 것입니다. 각 파일 유형에 대해 현대적인 언어 모델은 별도의 스크립트를 생성합니다. 여러 유형의 XML이 있는 경우, 여러 구문 분석 스크립트를 유지하는 데 이미 상당한 문제가 발생합니다.
위 문제는 일반적으로 전문 ETL 도구를 사용하여 해결됩니다. 이 글에서는 SmartXML이라는 ETL 도구를 살펴보겠습니다. SmartXML은 XML을 관계형 표현으로 변환하는 것도 지원하지만, 여기서는 XML을 MongoDB로 업로드하는 과정만 살펴볼 것입니다.
실제 XML은 매우 크고 복잡할 수 있습니다. 본 기사는 입문용 기사이므로 다음과 같은 상황을 해석할 것입니다:
- 모든 XML이 동일한 구조를 가지고 있는 경우;
- XML의 논리적 모델은 MongoDB의 저장 모델과 동일합니다;
- 추출된 필드는 복잡한 처리가 필요하지 않습니다;
그러한 경우는 나중에 다루겠지만, 우선 간단한 예제를 살펴보겠습니다:
<marketingData>
<customer>
<name>John Smith</name>
<email>[email protected]</email>
<purchases>
<purchase>
<product>Smartphone</product>
<category>Electronics</category>
<price>700</price>
<store>TechWorld</store>
<location>New York</location>
<purchaseDate>2025-01-10</purchaseDate>
</purchase>
<purchase>
<product>Wireless Earbuds</product>
<category>Audio</category>
<price>150</price>
<store>GadgetStore</store>
<location>New York</location>
<purchaseDate>2025-01-11</purchaseDate>
</purchase>
</purchases>
<importantInfo>
<loyaltyStatus>Gold</loyaltyStatus>
<age>34</age>
<gender>Male</gender>
<membershipID>123456</membershipID>
</importantInfo>
<lessImportantInfo>
<browser>Chrome</browser>
<deviceType>Mobile</deviceType>
<newsletterSubscribed>true</newsletterSubscribed>
</lessImportantInfo>
</customer>
<customer>
<name>Jane Doe</name>
<email>[email protected]</email>
<purchases>
<purchase>
<product>Laptop</product>
<category>Electronics</category>
<price>1200</price>
<store>GadgetStore</store>
<location>San Francisco</location>
<purchaseDate>2025-01-12</purchaseDate>
</purchase>
<purchase>
<product>USB-C Adapter</product>
<category>Accessories</category>
<price>30</price>
<store>TechWorld</store>
<location>San Francisco</location>
<purchaseDate>2025-01-13</purchaseDate>
</purchase>
<purchase>
<product>Keyboard</product>
<category>Accessories</category>
<price>80</price>
<store>OfficeMart</store>
<location>San Francisco</location>
<purchaseDate>2025-01-14</purchaseDate>
</purchase>
</purchases>
<importantInfo>
<loyaltyStatus>Silver</loyaltyStatus>
<age>28</age>
<gender>Female</gender>
<membershipID>654321</membershipID>
</importantInfo>
<lessImportantInfo>
<browser>Safari</browser>
<deviceType>Desktop</deviceType>
<newsletterSubscribed>false</newsletterSubscribed>
</lessImportantInfo>
</customer>
<customer>
<name>Michael Johnson</name>
<email>[email protected]</email>
<purchases>
<purchase>
<product>Headphones</product>
<category>Audio</category>
<price>150</price>
<store>AudioZone</store>
<location>Chicago</location>
<purchaseDate>2025-01-05</purchaseDate>
</purchase>
</purchases>
<importantInfo>
<loyaltyStatus>Bronze</loyaltyStatus>
<age>40</age>
<gender>Male</gender>
<membershipID>789012</membershipID>
</importantInfo>
<lessImportantInfo>
<browser>Firefox</browser>
<deviceType>Tablet</deviceType>
<newsletterSubscribed>true</newsletterSubscribed>
</lessImportantInfo>
</customer>
<customer>
<name>Emily Davis</name>
<email>[email protected]</email>
<purchases>
<purchase>
<product>Running Shoes</product>
<category>Sportswear</category>
<price>120</price>
<store>FitShop</store>
<location>Los Angeles</location>
<purchaseDate>2025-01-08</purchaseDate>
</purchase>
<purchase>
<product>Yoga Mat</product>
<category>Sportswear</category>
<price>40</price>
<store>FitShop</store>
<location>Los Angeles</location>
<purchaseDate>2025-01-09</purchaseDate>
</purchase>
</purchases>
<importantInfo>
<loyaltyStatus>Gold</loyaltyStatus>
<age>25</age>
<gender>Female</gender>
<membershipID>234567</membershipID>
</importantInfo>
<lessImportantInfo>
<browser>Edge</browser>
<deviceType>Mobile</deviceType>
<newsletterSubscribed>false</newsletterSubscribed>
</lessImportantInfo>
</customer>
<customer>
<name>Robert Brown</name>
<email>[email protected]</email>
<purchases>
<purchase>
<product>Smartwatch</product>
<category>Wearable</category>
<price>250</price>
<store>GadgetPlanet</store>
<location>Boston</location>
<purchaseDate>2025-01-07</purchaseDate>
</purchase>
<purchase>
<product>Fitness Band</product>
<category>Wearable</category>
<price>100</price>
<store>HealthMart</store>
<location>Boston</location>
<purchaseDate>2025-01-08</purchaseDate>
</purchase>
</purchases>
<importantInfo>
<loyaltyStatus>Silver</loyaltyStatus>
<age>37</age>
<gender>Male</gender>
<membershipID>345678</membershipID>
</importantInfo>
<lessImportantInfo>
<browser>Chrome</browser>
<deviceType>Mobile</deviceType>
<newsletterSubscribed>true</newsletterSubscribed>
</lessImportantInfo>
</customer>
</marketingData>
이 예제에서는 전체 XML이 아닌 실제 목적에 맞는 필드만 MongoDB에 업로드할 것입니다.
새 프로젝트 만들기
GUI에서 새 프로젝트를 만드는 것이 권장됩니다. 이렇게 하면 필요한 폴더 구조와 파싱 규칙이 자동으로 생성됩니다. 프로젝트 구조에 대한 전체 설명은 공식 문서에서 확인할 수 있습니다.
이 기사에 설명된 모든 매개변수는 그래픽 모드에서 구성할 수 있지만, 명확성을 위해 텍스트 표현에 집중하겠습니다.
프로젝트 설정이 포함된 config.txt
파일 외에도 배치 작업을 위한 job.txt, 프로젝트 자체는 다음으로 구성됩니다:
- 프로젝트 폴더
templates/data-templates.red
에 위치한 중간 내부SmartDOM
뷰의 템플릿. SmartDOM
자체의 처리 및 변환 규칙,rules
폴더에 위치합니다.
data-templates.red
의 구조를 살펴보겠습니다:
#[
sample: #[
marketing_data: #[
customers: [
customer: [
name: none
email: none
purchases: [
purchase: [
product: none
category: none
price: none
store: none
location: none
purchase_date: none
]
]
]
]
]
]
]
참고
- 이름
sample
은 카테고리의 이름이며, 중요하지 않습니다. marketing_data
는 하위 카테고리의 이름입니다. 최소한 하나의 코드 하위 카테고리(하위 유형)가 필요합니다.- 중간 뷰 이름은 XML 태그 이름과 정확히 일치할 필요가 없습니다. 이 예제에서는 의도적으로
snake_case
스타일을 사용했습니다.
규칙 추출
규칙은 프로젝트 폴더의 rules
디렉토리에 위치해 있습니다.
MongoDB와 작업할 때 우리는 두 가지 규칙에만 관심을 가질 것입니다:
tags-matching-rules.red
— XML 태그 트리와 SmartDOM 간의 일치를 설정합니다.grow-rules.red
— SmartDOM 노드와 실제 XML 노드 간의 관계를 설명합니다.
sample: [
purchase: ["purchase"]
customer: ["customer"]
]
핵심은 SmartDOM에서의 노드 이름이며, 값은 실제 XML 파일에서의 노드 철자 변형을 포함하는 배열이 될 것입니다. 우리의 예제에서는 이러한 이름이 동일합니다.
무시된 태그
위의 예제에서 MongoDB에 불필요한 데이터를 로드하지 않기 위해, 우리는 ignores
폴더에 섹션마다 하나씩 파일을 생성합니다. 이 파일들은 추출 중에 건너뛸 태그 목록을 포함합니다. 우리의 예제에서는 sample.txt
파일이 포함됩니다:
["marketingData" "customer" "lessImportantInfo" "browser"]
["marketingData" "customer" "lessImportantInfo" "deviceType"]
["marketingData" "customer" "lessImportantInfo" "newsletterSubscribed"]
결과적으로 형태 분석 후, 중간 표현은 다음 형태를 취합니다:
customers: [
customer: [
name: "John Smith"
email: "[email protected]"
loyalty_status: "Gold"
age: "34"
gender: "Male"
membership_id: "123456"
purchases: [
purchase: [
product: "Smartphone"
category: "Electronics"
price: "700"
store: "TechWorld"
location: "New York"
purchase_date: "2025-01-10"
]
]
]
]
형태 분석 후에는 처음 발견된 노드의 데이터만 포함된 최소한의 표현이 표시됩니다.
생성될 JSON 파일은 다음과 같습니다:
{
"customers": [
{
"name": "John Smith",
"email": "[email protected]",
"loyalty_status": "Gold",
"age": "34",
"gender": "Male",
"membership_id": "123456",
"purchases": [
{
"product": "Smartphone",
"category": "Electronics",
"price": "700",
"store": "TechWorld",
"location": "New York",
"purchase_date": "2025-01-10"
},
{
"product": "Wireless Earbuds",
"category": "Audio",
"price": "150",
"store": "GadgetStore",
"location": "New York",
"purchase_date": "2025-01-11"
}
]
},
{
"name": "Jane Doe",
"email": "[email protected]",
"loyalty_status": "Silver",
"age": "28",
"gender": "Female",
"membership_id": "654321",
"purchases": [
{
"product": "Laptop",
"category": "Electronics",
"price": "1200",
"store": "GadgetStore",
"location": "San Francisco",
"purchase_date": "2025-01-12"
},
{
"product": "USB-C Adapter",
"category": "Accessories",
"price": "30",
"store": "TechWorld",
"location": "San Francisco",
"purchase_date": "2025-01-13"
},
{
"product": "Keyboard",
"category": "Accessories",
"price": "80",
"store": "OfficeMart",
"location": "San Francisco",
"purchase_date": "2025-01-14"
}
]
},
{
"name": "Michael Johnson",
"email": "[email protected]",
"loyalty_status": "Bronze",
"age": "40",
"gender": "Male",
"membership_id": "789012",
"purchases": [
{
"product": "Headphones",
"category": "Audio",
"price": "150",
"store": "AudioZone",
"location": "Chicago",
"purchase_date": "2025-01-05"
}
]
},
{
"name": "Emily Davis",
"email": "[email protected]",
"loyalty_status": "Gold",
"age": "25",
"gender": "Female",
"membership_id": "234567",
"purchases": [
{
"product": "Running Shoes",
"category": "Sportswear",
"price": "120",
"store": "FitShop",
"location": "Los Angeles",
"purchase_date": "2025-01-08"
},
{
"product": "Yoga Mat",
"category": "Sportswear",
"price": "40",
"store": "FitShop",
"location": "Los Angeles",
"purchase_date": "2025-01-09"
}
]
},
{
"name": "Robert Brown",
"email": "[email protected]",
"loyalty_status": "Silver",
"age": "37",
"gender": "Male",
"membership_id": "345678",
"purchases": [
{
"product": "Smartwatch",
"category": "Wearable",
"price": "250",
"store": "GadgetPlanet",
"location": "Boston",
"purchase_date": "2025-01-07"
},
{
"product": "Fitness Band",
"category": "Wearable",
"price": "100",
"store": "HealthMart",
"location": "Boston",
"purchase_date": "2025-01-08"
}
]
}
]
}
MongoDB 연결 구성
MongoDB는 직접적인 HTTP 데이터 삽입을 지원하지 않으므로 중개 서비스가 필요합니다.
의존성을 설치해 봅시다: pip install flask pymongo
.
서비스 자체:
from flask import Flask, request, jsonify
from pymongo import MongoClient
import json
app = Flask(__name__)
# Connection to MongoDB
client = MongoClient('mongodb://localhost:27017')
db = client['testDB']
collection = db['testCollection']
route('/insert', methods=['POST']) .
def insert_document():
try:
# Flask will automatically parse JSON if Content-Type: application/json
data = request.get_json()
if not data:
return jsonify({"error": "Empty JSON payload"}), 400
result = collection.insert_one(data)
return jsonify({"insertedId": str(result.inserted_id)}), 200
except Exception as e:
import traceback
print(traceback.format_exc())
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(port=3000)
config.txt
파일에 MongoDB 연결 설정을 설정할 것입니다 ( nosql-url
참조):
job-number: 1
root-xml-folder: "D:/data/data-samples"
xml-filling-stat: false ; table: filling_percent_stat should exists
ignore-namespaces: false
ignore-tag-attributes: false
use-same-morphology-for-same-file-name-pattern: false
skip-schema-version-tag: true
use-same-morphology-for-all-files-in-folder: false
delete-data-before-insert: none
connect-to-db-at-project-opening: true
source-database: "SQLite" ; available values: PostgreSQL/SQLite
target-database: "SQLite" ; available values: PostgreSQL/SQLite/NoSQL
bot-chatID: ""
bot-token: ""
telegram-notifications: true
db-driver: ""
db-server: "127.0.0.1"
db-port: ""
db-name: ""
db-user: ""
db-pass: ""
sqlite-driver-name: "SQLite3 ODBC Driver"
sqlite-db-path: ""
nosql-url: "http://127.0.0.1:3000/insert"
append-subsection-name-to-nosql-url: false
no-sql-login: "" ; login and pass are empty
no-sql-pass: ""
MongoDB는 데이터베이스 및 컬렉션이 없는 경우 동일한 이름으로 자동으로 생성됩니다. 그러나 이 동작은 오류를 발생시킬 수 있으며, 기본적으로 비활성화하는 것이 좋습니다.
서비스 자체를 실행해 봅시다:
python .\app.py
다음, 파싱을 클릭하고, 그런 다음 JSON을 NoSQL로 전송을 클릭하세요.
모든 편리한 방법으로 MongoDB 콘솔에 연결하고 다음 명령을 실행하십시오:
show databases
admin 40.00 KiB
config 72.00 KiB
local 72.00 KiB
testDB 72.00 KiB
use testDB
switched to db testDB
db.testCollection.find().pretty()
결과는 다음과 같아야 합니다:
{
_id: ObjectId('278e1b2c7c1823d4fde120ef'),
customers: [
{
name: 'John Smith',
email: '[email protected]',
loyalty_status: 'Gold',
age: '34',
gender: 'Male',
membership_id: '123456',
purchases: [
{
product: 'Smartphone',
category: 'Electronics',
price: '700',
store: 'TechWorld',
location: 'New York',
purchase_date: '2025-01-10'
},
{
product: 'Wireless Earbuds',
category: 'Audio',
price: '150',
store: 'GadgetStore',
location: 'New York',
purchase_date: '2025-01-11'
}
]
},
{
name: 'Jane Doe',
email: '[email protected]',
loyalty_status: 'Silver',
age: '28',
gender: 'Female',
membership_id: '654321',
purchases: [
{
product: 'Laptop',
category: 'Electronics',
price: '1200',
store: 'GadgetStore',
location: 'San Francisco',
purchase_date: '2025-01-12'
},
{
product: 'USB-C Adapter',
category: 'Accessories',
price: '30',
store: 'TechWorld',
location: 'San Francisco',
purchase_date: '2025-01-13'
},
{
product: 'Keyboard',
category: 'Accessories',
price: '80',
store: 'OfficeMart',
location: 'San Francisco',
purchase_date: '2025-01-14'
}
]
},
{
name: 'Michael Johnson',
email: '[email protected]',
loyalty_status: 'Bronze',
age: '40',
gender: 'Male',
membership_id: '789012',
purchases: [
{
product: 'Headphones',
category: 'Audio',
price: '150',
store: 'AudioZone',
location: 'Chicago',
purchase_date: '2025-01-05'
}
]
},
{
name: 'Emily Davis',
email: '[email protected]',
loyalty_status: 'Gold',
age: '25',
gender: 'Female',
membership_id: '234567',
purchases: [
{
product: 'Running Shoes',
category: 'Sportswear',
price: '120',
store: 'FitShop',
location: 'Los Angeles',
purchase_date: '2025-01-08'
},
{
product: 'Yoga Mat',
category: 'Sportswear',
price: '40',
store: 'FitShop',
location: 'Los Angeles',
purchase_date: '2025-01-09'
}
]
},
{
name: 'Robert Brown',
email: '[email protected]',
loyalty_status: 'Silver',
age: '37',
gender: 'Male',
membership_id: '345678',
purchases: [
{
product: 'Smartwatch',
category: 'Wearable',
price: '250',
store: 'GadgetPlanet',
location: 'Boston',
purchase_date: '2025-01-07'
},
{
product: 'Fitness Band',
category: 'Wearable',
price: '100',
store: 'HealthMart',
location: 'Boston',
purchase_date: '2025-01-08'
}
]
}
]
}
마무리
이 예에서는 어떻게 코드를 작성하지 않고도 XML 파일을 MongoDB에 자동으로 업로드할 수 있는지를 볼 수 있었습니다. 예제는 하나의 파일만을 고려하지만, 하나의 프로젝트 내에서 많은 유형과 하위 유형의 다른 구조를 가진 파일을 다루고, 유형 변환과 실시간으로 필드 값을 처리하기 위해 외부 서비스를 사용하는 등의 복잡한 조작을 수행할 수 있습니다. 이는 XML에서 데이터를 추출하는 것 뿐만 아니라, 일부 값들을 외부 API를 통해 처리하고, 대규모 언어 모델을 사용하는 것도 가능하게 합니다.