Как передавать и парсить JSON/JSONB из C# в PL/Python функцию через Npgsql?
Передача данных в формате JSON / JSONB из C# в PL/Python — один из наиболее эффективных подходов: C# сериализует объект в JSON, Npgsql передает его в PostgreSQL как native-тип
jsonb, а PL/Python автоматически или с помощью стандартного модуля json превращает его в родной словарь (dict) или список (list).1. Подготовка функции в PostgreSQL (PL/Python)
Функция принимает тип
jsonb (или json). В PL/Python аргументы типа json/jsonb передаются в виде обычных строк, поэтому внутри их нужно распарсить с помощью стандартного модуля json.SQL
CREATE OR REPLACE FUNCTION process_user_data(payload jsonb)
RETURNS TABLE(user_id int, status text, is_valid boolean) AS $$
import json
# PL/Python получает json/jsonb как строку
data = json.loads(payload)
users = data.get("users", [])
result = []
for user in users:
u_id = user.get("id")
email = user.get("email", "")
# Простая проверка/обработка
is_valid = "@" in email
status = "Active" if user.get("age", 0) >= 18 else "Pending"
result.append({
"user_id": u_id,
"status": status,
"is_valid": is_valid
})
return result
$$ LANGUAGE plpython3u;
2. Вызов из C# с помощью Npgsql и System.Text.Json
В современных версиях Npgsql передача JSON выполняется через встроенную поддержку
System.Text.Json или явное указание типа NpgsqlDbType.Jsonb.Вариант 1: Использование System.Text.Json (рекомендуемый)
C#
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
public class JsonPostgresExample
{
private const string ConnectionString = "Host=localhost;Database=mydb;Username=postgres;Password=secret";
public async Task ProcessDataAsync()
{
// 1. Формируем C# объект для передачи
var requestData = new
{
users = new[]
{
new { id = 101, email = "alex@example.com", age = 25 },
new { id = 102, email = "invalid_email", age = 16 }
}
};
// 2. Сериализуем объект в строку JSON
string jsonPayload = JsonSerializer.Serialize(requestData);
await using var conn = new NpgsqlConnection(ConnectionString);
await conn.OpenAsync();
const string sql = "SELECT user_id, status, is_valid FROM process_user_data(@payload)";
await using var cmd = new NpgsqlCommand(sql, conn);
// 3. Добавляем параметр с явным указанием NpgsqlDbType.Jsonb
cmd.Parameters.Add(new NpgsqlParameter("payload", NpgsqlDbType.Jsonb)
{
Value = jsonPayload
});
// 4. Читаем результат
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var userId = reader.GetInt32(0);
var status = reader.GetString(1);
var isValid = reader.GetBoolean(2);
Console.WriteLine($"ID: {userId} | Статус: {status} | Валиден: {isValid}");
}
}
}
3. Обратный сценарий: Возврат JSONB из PL/Python в C#
Если функция PL/Python должна вернуть сложный JSON-объект обратно в C#:
Функция в PostgreSQL:
SQL
CREATE OR REPLACE FUNCTION get_system_stats()
RETURNS jsonb AS $$
import json
stats = {
"server_time": "2026-09-12",
"metrics": {
"cpu_usage": 42.5,
"active_connections": 12
},
"tags": ["prod", "primary"]
}
# Возвращаем JSON-строку
return json.dumps(stats)
$$ LANGUAGE plpython3u;
Код чтения в C#:
C#
public async Task GetStatsAsync()
{
await using var conn = new NpgsqlConnection(ConnectionString);
await conn.OpenAsync();
const string sql = "SELECT get_system_stats()";
await using var cmd = new NpgsqlCommand(sql, conn);
var rawJson = (string)await cmd.ExecuteScalarAsync();
// Десериализуем JSON в C# объект
using var doc = JsonDocument.Parse(rawJson);
var cpu = doc.RootElement
.GetProperty("metrics")
.GetProperty("cpu_usage")
.GetDouble();
Console.WriteLine($"CPU Usage: {cpu}%");
}
Ключевые моменты при работе с JSON/JSONB:
-
Тип параметра в C#: При передаче строкового JSON всегда указывайте
NpgsqlDbType.Jsonb(илиNpgsqlDbType.Json), иначе Npgsql передаст строку как обычныйtext, и Postgres выдаст ошибку несоответствия типов. -
Сериализация внутри PL/Python:
-
При приеме
jsonbв Python аргумент является строкойstr— применяйтеjson.loads(arg). -
При возврате
jsonbотправляйте строку — применяйтеjson.dumps(obj).
-
No comments to display
No comments to display