UNPKG

@blocktion/json-to-table

Version:

A powerful, modular React component for converting JSON data to navigable tables with advanced features like automatic column detection, theming, and sub-table navigation. Part of the Blocktion SaaS project ecosystem.

136 lines (135 loc) 4.76 kB
import { ArrayAnalyzer } from "../utils/arrayUtils"; import { ObjectUtils } from "../utils/objectUtils"; export class DataProcessor { static getInstance() { if (!DataProcessor.instance) { DataProcessor.instance = new DataProcessor(); } return DataProcessor.instance; } processData(data) { const parsed = this.parseJsonStrings(data); const processedForRepeatedAttributes = ObjectUtils.processArrayForRepeatedAttributes(parsed); const validation = this.validateData(processedForRepeatedAttributes); const analyzed = this.analyzeStructure(processedForRepeatedAttributes); const metadata = this.generateMetadata(analyzed, validation); return { raw: data, parsed, validated: validation.isValid ? processedForRepeatedAttributes : [], analyzed, metadata, }; } parseJsonStrings(data) { return data.map((item) => this.tryParseJSON(item)); } tryParseJSON(value) { if (typeof value !== "string") return value; const trimmed = value.trim(); if (trimmed.length > 2 && ((trimmed.startsWith("[") && trimmed.endsWith("]")) || (trimmed.startsWith("{") && trimmed.endsWith("}")))) { try { return JSON.parse(value); } catch (error) { return value; } } return value; } validateData(data) { const errors = []; const warnings = []; if (!Array.isArray(data)) { errors.push("Data must be an array"); return { isValid: false, errors, warnings }; } if (data.length === 0) { warnings.push("Data array is empty"); } data.forEach((item, index) => { if (item === null || item === undefined) { warnings.push(`Row ${index} is null or undefined`); } else if (typeof item !== "object") { warnings.push(`Row ${index} is not an object (type: ${typeof item})`); } }); return { isValid: errors.length === 0, errors, warnings, }; } analyzeStructure(data) { if (data.length === 0) { return { isArray: false, isObject: false, arrayContentType: "empty", depth: 0, sampleData: [], }; } const sampleData = data.slice(0, 5); const firstItem = sampleData[0]; // Check if the data itself is an array of arrays if (Array.isArray(firstItem)) { const arrayAnalysis = ArrayAnalyzer.analyzeContent(firstItem); return { isArray: true, isObject: false, arrayContentType: arrayAnalysis.type, depth: 1, sampleData: firstItem, }; } // Check if the data is an array of objects (most common case) if (typeof firstItem === "object" && firstItem !== null) { const objectKeys = Object.keys(firstItem); // Check if any property contains arrays let hasNestedArrays = false; let maxDepth = 1; for (const item of sampleData) { if (typeof item === "object" && item !== null) { const obj = item; for (const [key, value] of Object.entries(obj)) { if (Array.isArray(value)) { hasNestedArrays = true; const arrayAnalysis = ArrayAnalyzer.analyzeContent(value); if (arrayAnalysis.type === "objects") { maxDepth = Math.max(maxDepth, 2); } } } } } return { isArray: false, isObject: true, objectKeys, depth: hasNestedArrays ? maxDepth : 1, sampleData: [firstItem], }; } return { isArray: false, isObject: false, depth: 0, sampleData: [firstItem], }; } generateMetadata(analysis, validation) { return { totalRows: analysis.sampleData.length, totalColumns: analysis.objectKeys?.length || 0, hasNestedData: analysis.depth > 1, maxDepth: analysis.depth, warnings: validation.warnings, errors: validation.errors, }; } }