mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-18 02:54:01 +01:00
Merge pull request #197 from bhavesh158/json-compare
feat: add JSON comparison tool
This commit is contained in:
37
package-lock.json
generated
37
package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@ffmpeg/util": "^0.12.2",
|
"@ffmpeg/util": "^0.12.2",
|
||||||
"@imgly/background-removal": "^1.6.0",
|
"@imgly/background-removal": "^1.6.0",
|
||||||
"@jimp/types": "^1.6.0",
|
"@jimp/types": "^1.6.0",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
"@playwright/test": "^1.45.0",
|
"@playwright/test": "^1.45.0",
|
||||||
@@ -2104,6 +2105,29 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@monaco-editor/loader": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@monaco-editor/react": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/loader": "^1.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.25.0 < 1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/base": {
|
"node_modules/@mui/base": {
|
||||||
"version": "5.0.0-beta.40",
|
"version": "5.0.0-beta.40",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
|
||||||
@@ -8902,6 +8926,13 @@
|
|||||||
"ufo": "^1.5.3"
|
"ufo": "^1.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.52.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
|
||||||
|
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/morsee": {
|
"node_modules/morsee": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/morsee/-/morsee-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/morsee/-/morsee-1.0.9.tgz",
|
||||||
@@ -11271,6 +11302,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/std-env": {
|
"node_modules/std-env": {
|
||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"@ffmpeg/util": "^0.12.2",
|
"@ffmpeg/util": "^0.12.2",
|
||||||
"@imgly/background-removal": "^1.6.0",
|
"@imgly/background-removal": "^1.6.0",
|
||||||
"@jimp/types": "^1.6.0",
|
"@jimp/types": "^1.6.0",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
"@playwright/test": "^1.45.0",
|
"@playwright/test": "^1.45.0",
|
||||||
|
|||||||
@@ -58,5 +58,10 @@
|
|||||||
"title": "What is JSON Validation?"
|
"title": "What is JSON Validation?"
|
||||||
},
|
},
|
||||||
"validJson": "✅ Valid JSON"
|
"validJson": "✅ Valid JSON"
|
||||||
|
},
|
||||||
|
"comparison": {
|
||||||
|
"title": "Compare JSON",
|
||||||
|
"description": "Compare two JSON objects to identify differences in structure and values.",
|
||||||
|
"shortDescription": "Find differences between two JSON objects"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,5 +106,10 @@
|
|||||||
},
|
},
|
||||||
"validJson": "✅ मान्य JSON",
|
"validJson": "✅ मान्य JSON",
|
||||||
"validationOptions": "मान्यता विकल्प"
|
"validationOptions": "मान्यता विकल्प"
|
||||||
|
},
|
||||||
|
"comparison": {
|
||||||
|
"title": "JSON तुलना करें",
|
||||||
|
"description": "दो JSON वस्तुओं की संरचना और मूल्यों में अंतर की पहचान करें।",
|
||||||
|
"shortDescription": "दो JSON वस्तुओं के बीच अंतर ढूंढें"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/components/input/ToolCodeInput.tsx
Normal file
73
src/components/input/ToolCodeInput.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
|
import React, { useContext, useRef } from 'react';
|
||||||
|
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||||
|
import InputHeader from '../InputHeader';
|
||||||
|
import InputFooter from './InputFooter';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Editor from '@monaco-editor/react';
|
||||||
|
import { globalInputHeight } from '../../config/uiConfig';
|
||||||
|
|
||||||
|
export default function ToolCodeInput({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
title = 'Input text',
|
||||||
|
language
|
||||||
|
}: {
|
||||||
|
title?: string;
|
||||||
|
value: string;
|
||||||
|
language: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(value)
|
||||||
|
.then(() => showSnackBar(t('toolTextInput.copied'), 'success'))
|
||||||
|
.catch((err) => {
|
||||||
|
showSnackBar(t('toolTextInput.copyFailed', { error: err }), 'error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = event.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const text = e.target?.result;
|
||||||
|
if (typeof text === 'string') {
|
||||||
|
onChange(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImportClick = () => {
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<InputHeader title={title || t('toolTextInput.input')} />
|
||||||
|
<Box height={globalInputHeight}>
|
||||||
|
<Editor
|
||||||
|
height={'87%'}
|
||||||
|
language={language}
|
||||||
|
value={value}
|
||||||
|
onChange={(value) => onChange(value ?? '')}
|
||||||
|
/>
|
||||||
|
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="*"
|
||||||
|
ref={fileInputRef}
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { tool as validateJson } from './validateJson/meta';
|
|||||||
import { tool as jsonToXml } from './json-to-xml/meta';
|
import { tool as jsonToXml } from './json-to-xml/meta';
|
||||||
import { tool as escapeJson } from './escape-json/meta';
|
import { tool as escapeJson } from './escape-json/meta';
|
||||||
import { tool as tsvToJson } from './tsv-to-json/meta';
|
import { tool as tsvToJson } from './tsv-to-json/meta';
|
||||||
|
import { tool as jsonComparison } from './json-comparison/meta';
|
||||||
|
|
||||||
export const jsonTools = [
|
export const jsonTools = [
|
||||||
validateJson,
|
validateJson,
|
||||||
@@ -13,5 +14,6 @@ export const jsonTools = [
|
|||||||
jsonStringify,
|
jsonStringify,
|
||||||
jsonToXml,
|
jsonToXml,
|
||||||
escapeJson,
|
escapeJson,
|
||||||
tsvToJson
|
tsvToJson,
|
||||||
|
jsonComparison
|
||||||
];
|
];
|
||||||
|
|||||||
89
src/pages/tools/json/json-comparison/index.tsx
Normal file
89
src/pages/tools/json/json-comparison/index.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import ToolContent from '@components/ToolContent';
|
||||||
|
import ToolCodeInput from '@components/input/ToolCodeInput';
|
||||||
|
import ToolTextResult from '@components/result/ToolTextResult';
|
||||||
|
import { compareJson } from './service';
|
||||||
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
|
import { Grid } from '@mui/material';
|
||||||
|
|
||||||
|
type InitialValuesType = {};
|
||||||
|
|
||||||
|
const initialValues: InitialValuesType = {};
|
||||||
|
|
||||||
|
export default function JsonComparison({ title }: ToolComponentProps) {
|
||||||
|
const [input1, setInput1] = useState<string>('');
|
||||||
|
const [input2, setInput2] = useState<string>('');
|
||||||
|
const [result, setResult] = useState<string>('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const compareInputs = () => {
|
||||||
|
try {
|
||||||
|
// Only compare if at least one input has content
|
||||||
|
if (input1.trim() || input2.trim()) {
|
||||||
|
const differences = compareJson(
|
||||||
|
input1 || '{}',
|
||||||
|
input2 || '{}',
|
||||||
|
'text'
|
||||||
|
);
|
||||||
|
setResult(differences);
|
||||||
|
} else {
|
||||||
|
setResult('');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setResult(
|
||||||
|
`Error: ${
|
||||||
|
error instanceof Error ? error.message : 'Invalid JSON format'
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
compareInputs();
|
||||||
|
}, [input1, input2]);
|
||||||
|
|
||||||
|
const handleInput1Change = (value: string | undefined) => {
|
||||||
|
setInput1(value ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInput2Change = (value: string) => {
|
||||||
|
setInput2(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolContent
|
||||||
|
title={title}
|
||||||
|
input={input1}
|
||||||
|
setInput={setInput1}
|
||||||
|
initialValues={initialValues}
|
||||||
|
getGroups={null}
|
||||||
|
compute={() => {}}
|
||||||
|
inputComponent={
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
|
<ToolCodeInput
|
||||||
|
title="First JSON"
|
||||||
|
value={input1}
|
||||||
|
onChange={handleInput1Change}
|
||||||
|
language={'json'}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
|
<ToolCodeInput
|
||||||
|
title="Second JSON"
|
||||||
|
language={'json'}
|
||||||
|
value={input2}
|
||||||
|
onChange={handleInput2Change}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={12} lg={4}>
|
||||||
|
<ToolTextResult
|
||||||
|
title="Differences"
|
||||||
|
value={result}
|
||||||
|
extension={'txt'}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
src/pages/tools/json/json-comparison/meta.ts
Normal file
15
src/pages/tools/json/json-comparison/meta.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const tool = defineTool('json', {
|
||||||
|
path: 'json-comparison',
|
||||||
|
icon: 'fluent:branch-compare-24-regular',
|
||||||
|
keywords: ['json', 'compare', 'diff', 'differences', 'match', 'validation'],
|
||||||
|
component: lazy(() => import('./index')),
|
||||||
|
|
||||||
|
i18n: {
|
||||||
|
name: 'json:comparison.title',
|
||||||
|
description: 'json:comparison.description',
|
||||||
|
shortDescription: 'json:comparison.shortDescription'
|
||||||
|
}
|
||||||
|
});
|
||||||
64
src/pages/tools/json/json-comparison/service.test.ts
Normal file
64
src/pages/tools/json/json-comparison/service.test.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { compareJson } from './service';
|
||||||
|
|
||||||
|
describe('compareJson', () => {
|
||||||
|
it('should identify missing properties', () => {
|
||||||
|
const json1 = '{"name": "John", "age": 30}';
|
||||||
|
const json2 = '{"name": "John"}';
|
||||||
|
|
||||||
|
expect(compareJson(json1, json2, 'text')).toContain(
|
||||||
|
'age: Missing in second JSON'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should identify value mismatches', () => {
|
||||||
|
const json1 = '{"name": "John", "age": 30}';
|
||||||
|
const json2 = '{"name": "John", "age": 25}';
|
||||||
|
|
||||||
|
expect(compareJson(json1, json2, 'text')).toContain(
|
||||||
|
'age: Mismatch: 30 != 25'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle nested objects', () => {
|
||||||
|
const json1 = '{"person": {"name": "John", "age": 30}}';
|
||||||
|
const json2 = '{"person": {"name": "Jane", "age": 30}}';
|
||||||
|
|
||||||
|
expect(compareJson(json1, json2, 'text')).toContain(
|
||||||
|
'person.name: Mismatch: John != Jane'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return JSON format when specified', () => {
|
||||||
|
const json1 = '{"name": "John", "age": 30}';
|
||||||
|
const json2 = '{"name": "Jane", "age": 25}';
|
||||||
|
|
||||||
|
const result = compareJson(json1, json2, 'json');
|
||||||
|
const parsed = JSON.parse(result);
|
||||||
|
|
||||||
|
expect(parsed).toHaveProperty('name');
|
||||||
|
expect(parsed).toHaveProperty('age');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle arrays', () => {
|
||||||
|
const json1 = '{"numbers": [1, 2, 3]}';
|
||||||
|
const json2 = '{"numbers": [1, 2, 4]}';
|
||||||
|
|
||||||
|
expect(compareJson(json1, json2, 'text')).toContain(
|
||||||
|
'numbers.2: Mismatch: 3 != 4'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "No differences found" for identical JSONs', () => {
|
||||||
|
const json1 = '{"name": "John", "age": 30}';
|
||||||
|
const json2 = '{"name": "John", "age": 30}';
|
||||||
|
|
||||||
|
expect(compareJson(json1, json2, 'text')).toBe('No differences found');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for invalid JSON', () => {
|
||||||
|
const json1 = '{"name": "John"';
|
||||||
|
const json2 = '{"name": "John"}';
|
||||||
|
|
||||||
|
expect(() => compareJson(json1, json2, 'text')).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
139
src/pages/tools/json/json-comparison/service.ts
Normal file
139
src/pages/tools/json/json-comparison/service.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
const fixTrailingCommas = (json: string): string => {
|
||||||
|
// Replace trailing commas in objects and arrays with proper JSON syntax
|
||||||
|
return json
|
||||||
|
.replace(/,\s*([}\]])/g, '$1') // Remove trailing commas in objects and arrays
|
||||||
|
.replace(/,\s*\n\s*([}\]])/g, '\n$1'); // Also handle when the closing bracket is on a new line
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryParseJSON = (
|
||||||
|
json: string
|
||||||
|
): { valid: boolean; data?: any; error?: string } => {
|
||||||
|
if (!json.trim()) {
|
||||||
|
return { valid: true, data: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to parse after fixing trailing commas
|
||||||
|
const fixedJson = fixTrailingCommas(json);
|
||||||
|
const data = JSON.parse(fixedJson);
|
||||||
|
return { valid: true, data };
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof SyntaxError ? error.message : 'Invalid JSON format';
|
||||||
|
// Extract line and column info from the error message if available
|
||||||
|
const match = errorMessage.match(/at line (\d+) column (\d+)/);
|
||||||
|
if (match) {
|
||||||
|
const [, line, column] = match;
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: `${errorMessage}\nLocation: Line ${line}, Column ${column}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: errorMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compareJson = (
|
||||||
|
json1: string,
|
||||||
|
json2: string,
|
||||||
|
format: 'text' | 'json'
|
||||||
|
): string => {
|
||||||
|
// Handle empty inputs
|
||||||
|
if (!json1.trim() && !json2.trim()) return '';
|
||||||
|
|
||||||
|
// Parse both JSON inputs
|
||||||
|
const parsed1 = tryParseJSON(json1);
|
||||||
|
const parsed2 = tryParseJSON(json2);
|
||||||
|
|
||||||
|
// Handle parsing errors
|
||||||
|
if (!parsed1.valid || !parsed2.valid) {
|
||||||
|
const errors = [];
|
||||||
|
if (!parsed1.valid) {
|
||||||
|
errors.push(`First JSON: ${parsed1.error}`);
|
||||||
|
}
|
||||||
|
if (!parsed2.valid) {
|
||||||
|
errors.push(`Second JSON: ${parsed2.error}`);
|
||||||
|
}
|
||||||
|
throw new Error(errors.join('\n\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the valid JSON objects
|
||||||
|
if (format === 'json') {
|
||||||
|
const diffs = findDifferencesJSON(parsed1.data, parsed2.data);
|
||||||
|
return JSON.stringify(diffs);
|
||||||
|
} else {
|
||||||
|
const differences = findDifferencesText(parsed1.data, parsed2.data);
|
||||||
|
if (differences.length === 0) {
|
||||||
|
return 'No differences found';
|
||||||
|
}
|
||||||
|
return differences.join('\n');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findDifferencesText = (
|
||||||
|
obj1: any,
|
||||||
|
obj2: any,
|
||||||
|
path: string[] = []
|
||||||
|
): string[] => {
|
||||||
|
const differences: string[] = [];
|
||||||
|
const processPath = (p: string[]): string =>
|
||||||
|
p.length ? p.join('.') : 'root';
|
||||||
|
|
||||||
|
// Compare all keys in obj1
|
||||||
|
for (const key in obj1) {
|
||||||
|
const currentPath = [...path, key];
|
||||||
|
|
||||||
|
if (!(key in obj2)) {
|
||||||
|
differences.push(`${processPath(currentPath)}: Missing in second JSON`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value1 = obj1[key];
|
||||||
|
const value2 = obj2[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof value1 === 'object' &&
|
||||||
|
value1 !== null &&
|
||||||
|
typeof value2 === 'object' &&
|
||||||
|
value2 !== null
|
||||||
|
) {
|
||||||
|
differences.push(...findDifferencesText(value1, value2, currentPath));
|
||||||
|
} else if (value1 !== value2) {
|
||||||
|
differences.push(
|
||||||
|
`${processPath(currentPath)}: Mismatch: ${value1} != ${value2}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for keys in obj2 that don't exist in obj1
|
||||||
|
for (const key in obj2) {
|
||||||
|
if (!(key in obj1)) {
|
||||||
|
const currentPath = [...path, key];
|
||||||
|
differences.push(`${processPath(currentPath)}: Missing in first JSON`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return differences;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findDifferencesJSON = (obj1: any, obj2: any): Record<string, string> => {
|
||||||
|
const result: Record<string, string> = {};
|
||||||
|
|
||||||
|
// Compare all properties
|
||||||
|
const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
|
||||||
|
|
||||||
|
for (const key of allKeys) {
|
||||||
|
if (!(key in obj1)) {
|
||||||
|
result[key] = 'Missing in first JSON';
|
||||||
|
} else if (!(key in obj2)) {
|
||||||
|
result[key] = 'Missing in second JSON';
|
||||||
|
} else if (obj1[key] !== obj2[key]) {
|
||||||
|
result[key] = `Mismatch: ${obj1[key]} != ${obj2[key]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user