728x90
1. 기본 개념
1.1 Jinja2에서 JSON 처리의 핵심 원리
- Jinja2는 템플릿 엔진으로, JSON 데이터를 문자열로 변환 후
from_json
필터로 파싱 - 직접적인 객체 조작보다는 문자열 기반 JSON 생성이 더 안정적
- 홈어시스턴트 등 제한된 환경에서는 일부 내장 함수 사용 불가
1.2 주요 필터 및 함수
{{ data | tojson }} # 객체를 JSON 문자열로 변환
{{ json_str | from_json }} # JSON 문자열을 객체로 파싱
{{ array | selectattr() }} # 배열에서 특정 속성 기준 필터링
{{ array | rejectattr() }} # 배열에서 특정 속성 기준 제외
{{ array | map() }} # 배열 각 요소에 함수 적용
2. 데이터 구조별 처리 방법
2.1 단순 객체 필터링
예시 데이터
{
"name": "김철수",
"age": 30,
"email": "kim@example.com",
"phone": null,
"address": "",
"active": true
}
null과 빈값 제거
{% set json_str %}{
{% for k, v in user_data.items() if v is not none and v != '' %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
}{% endset %}
{{ json_str | from_json }}
결과
{
"name": "김철수",
"age": "30",
"email": "kim@example.com",
"active": "true"
}
2.2 배열 데이터 필터링
예시 데이터
[
{
"id": 1,
"name": "상품A",
"price": 10000,
"category": "전자제품",
"stock": null,
"description": ""
},
{
"id": 2,
"name": "상품B",
"price": null,
"category": "의류",
"stock": 50,
"description": "좋은 상품"
}
]
배열 내 객체들의 null/빈값 제거
{% set json_str %}[
{% for item in products %}
{% if not loop.first %},{% endif %}
{
{% for k, v in item.items() if v is not none and v != '' %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
}
{% endfor %}
]{% endset %}
{{ json_str | from_json }}
2.3 중첩 구조 처리
예시 데이터
{
"company": "테크코프",
"departments": [
{
"name": "개발팀",
"manager": "김팀장",
"employees": [
{"name": "이개발", "role": "시니어", "salary": null},
{"name": "박주니어", "role": null, "salary": 3000}
],
"budget": null
}
],
"founded": 2020,
"revenue": null
}
중첩 구조 정리
{% set json_str %}{
{% for k, v in company_data.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}":
{% if k == 'departments' %}
[
{% for dept in v %}
{% if not loop.first %},{% endif %}
{
{% for dk, dv in dept.items() if dv is not none %}
{% if not loop.first %},{% endif %}
"{{ dk }}":
{% if dk == 'employees' %}
[
{% for emp in dv %}
{% if not loop.first %},{% endif %}
{
{% for ek, ev in emp.items() if ev is not none %}
{% if not loop.first %},{% endif %}
"{{ ek }}": "{{ ev }}"
{% endfor %}
}
{% endfor %}
]
{% else %}
"{{ dv }}"
{% endif %}
{% endfor %}
}
{% endfor %}
]
{% else %}
"{{ v }}"
{% endif %}
{% endfor %}
}{% endset %}
{{ json_str | from_json }}
3. 고급 필터링 기법
3.1 조건부 필터링
특정 조건을 만족하는 데이터만 포함
{% set json_str %}[
{% for item in products if item.price is not none and item.price > 5000 %}
{% if not loop.first %},{% endif %}
{
{% for k, v in item.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
}
{% endfor %}
]{% endset %}
{{ json_str | from_json }}
3.2 데이터 변환
데이터 타입 변환 및 계산
{% set json_str %}[
{% for item in products %}
{% if not loop.first %},{% endif %}
{
"id": {{ item.id }},
"name": "{{ item.name }}",
"price": {{ item.price if item.price else 0 }},
"discounted_price": {{ (item.price * 0.9) | round(2) if item.price else 0 }},
"category": "{{ item.category }}",
"in_stock": {% if item.stock and item.stock > 0 %}true{% else %}false{% endif %}
}
{% endfor %}
]{% endset %}
{{ json_str | from_json }}
3.3 그룹화 및 집계
카테고리별 상품 그룹화
{% set categories = products | groupby('category') %}
{% set json_str %}{
{% for category, items in categories %}
{% if not loop.first %},{% endif %}
"{{ category }}": {
"count": {{ items | list | length }},
"products": [
{% for item in items %}
{% if not loop.first %},{% endif %}
{
{% for k, v in item.items() if v is not none and k != 'category' %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
}
{% endfor %}
]
}
{% endfor %}
}{% endset %}
{{ json_str | from_json }}
300x250
4. 실제 사용 사례
4.1 API 응답 데이터 정리
원본 API 응답
{
"status": "success",
"data": {
"users": [
{
"id": 1,
"username": "user1",
"email": "user1@example.com",
"profile": {
"first_name": "홍",
"last_name": "길동",
"bio": null,
"avatar": null,
"created_at": "2024-01-01"
},
"settings": {
"notifications": true,
"privacy": null,
"theme": "dark"
}
}
],
"pagination": {
"page": 1,
"per_page": 10,
"total": null,
"has_more": false
}
},
"message": null,
"errors": null
}
정리된 응답 생성
{% set json_str %}{
"status": "{{ api_response.status }}",
"users": [
{% for user in api_response.data.users %}
{% if not loop.first %},{% endif %}
{
"id": {{ user.id }},
"username": "{{ user.username }}",
"email": "{{ user.email }}",
"profile": {
{% for k, v in user.profile.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
},
"settings": {
{% for k, v in user.settings.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": {% if v is sameas true or v is sameas false %}{{ v | lower }}{% else %}"{{ v }}"{% endif %}
{% endfor %}
}
}
{% endfor %}
],
"pagination": {
{% for k, v in api_response.data.pagination.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": {% if v is sameas true or v is sameas false %}{{ v | lower }}{% elif v is number %}{{ v }}{% else %}"{{ v }}"{% endif %}
{% endfor %}
}
}{% endset %}
{{ json_str | from_json }}
4.2 센서 데이터 집계
여러 센서 데이터 통합
{% set sensors = [
{"name": "온도", "value": 23.5, "unit": "°C", "status": "normal", "error": null},
{"name": "습도", "value": null, "unit": "%", "status": "error", "error": "센서 오류"},
{"name": "압력", "value": 1013, "unit": "hPa", "status": "normal", "error": null}
] %}
{% set json_str %}{
"timestamp": "{{ now().isoformat() }}",
"summary": {
"total_sensors": {{ sensors | length }},
"active_sensors": {{ sensors | selectattr('value') | list | length }},
"error_count": {{ sensors | selectattr('status', 'equalto', 'error') | list | length }}
},
"sensors": [
{% for sensor in sensors %}
{% if not loop.first %},{% endif %}
{
{% for k, v in sensor.items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": {% if v is number %}{{ v }}{% else %}"{{ v }}"{% endif %}
{% endfor %}
}
{% endfor %}
]
}{% endset %}
{{ json_str | from_json }}
5. 성능 최적화 및 모범 사례
5.1 효율적인 필터링
{# 좋은 예: 조건을 먼저 체크 #}
{% for item in large_dataset if item.active and item.value %}
...
{% endfor %}
{# 나쁜 예: 모든 아이템을 순회 후 조건 체크 #}
{% for item in large_dataset %}
{% if item.active and item.value %}
...
{% endif %}
{% endfor %}
5.2 변수 활용으로 코드 간소화
{% set clean_fields = ['id', 'name', 'email', 'active'] %}
{% for item in data %}
{
{% for field in clean_fields if item[field] is defined and item[field] is not none %}
"{{ field }}": "{{ item[field] }}"{% if not loop.last %},{% endif %}
{% endfor %}
}
{% endfor %}
5.3 에러 처리
{% set json_str %}{
{% for item in data %}
{% if item is mapping %}
{% for k, v in item.items() if v is not none %}
"{{ k }}": "{{ v | string | replace('"', '\\"') }}"
{% endfor %}
{% endif %}
{% endfor %}
}{% endset %}
{# JSON 파싱 시 에러 처리 #}
{% set result = json_str | from_json %}
{% if result %}
{{ result }}
{% else %}
{"error": "JSON 파싱 실패"}
{% endif %}
6. 홈어시스턴트 특화 기법
6.1 센서 속성 정리
# configuration.yaml
sensor:
- platform: template
sensors:
clean_weather_data:
value_template: "OK"
attribute_templates:
processed_data: >
{% set json_str %}{
{% for k, v in state_attr('weather.home', 'forecast')[0].items() if v is not none %}
{% if not loop.first %},{% endif %}
"{{ k }}": "{{ v }}"
{% endfor %}
}{% endset %}
{{ json_str | from_json }}
6.2 다중 엔티티 데이터 병합
{% set entities = ['sensor.temp1', 'sensor.temp2', 'sensor.humidity'] %}
{% set json_str %}[
{% for entity_id in entities %}
{% set entity = states[entity_id] %}
{% if entity and entity.state != 'unavailable' %}
{% if not loop.first %},{% endif %}
{
"entity_id": "{{ entity_id }}",
"name": "{{ entity.attributes.friendly_name | default(entity_id) }}",
"value": "{{ entity.state }}",
"unit": "{{ entity.attributes.unit_of_measurement | default('') }}",
"last_updated": "{{ entity.last_updated }}"
}
{% endif %}
{% endfor %}
]{% endset %}
{{ json_str | from_json }}
이러한 방식들을 조합하여 복잡한 JSON 데이터도 효과적으로 처리할 수 있습니다. 핵심은 문자열 기반 JSON 생성과 적절한 조건 필터링, 그리고 최종적인 from_json
파싱입니다.
728x90
그리드형(광고전용)
댓글