diff --git a/README.md b/README.md index 577106e..e0247b7 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Open-source data conversion utils for devs who don't like ads. Simple, lightweig Here is the list of all utilities: - [CSV to JSON](https://jam.dev/utilities/csv-to-json) +- [TSV to JSON](https://jam.dev/utilities/tsv-to-json) - [Base64 Encode/Decode](https://jam.dev/utilities/base-64-encoder) - [JSON Formatter](https://jam.dev/utilities/json-formatter) - [YAML to JSON](https://jam.dev/utilities/yaml-to-json) @@ -36,6 +37,7 @@ Here is the list of all utilities: - [Image to Base64 Converter](https://jam.dev/utilities/image-to-base64) - [Base64 to Image Converter](https://jam.dev/utilities/base64-to-image) - [JSON to CSV](https://jam.dev/utilities/json-to-csv) +- [JSON to TSV](https://jam.dev/utilities/json-to-tsv) - [HAR file viewer](https://jam.dev/utilities/har-file-viewer) - [JSON to YAML](https://jam.dev/utilities/json-to-yaml) - [Number Base Changer](https://jam.dev/utilities/number-base-changer) diff --git a/components/seo/JsonToTsvSEO.tsx b/components/seo/JsonToTsvSEO.tsx new file mode 100644 index 0000000..e9dc22a --- /dev/null +++ b/components/seo/JsonToTsvSEO.tsx @@ -0,0 +1,116 @@ +import Link from "next/link"; + +export default function JsonToTsvSEO() { + return ( +
+
+

+ This free tool offers a quick and easy way to convert JSON files into + TSV format. If you work with data analysis, spreadsheets, or need to + import data into various applications, you can use Jam's JSON to TSV + converter to transform structured JSON data into tab-separated format. +

+
+ +
+

+ Simply paste your JSON data and get the TSV result. Built with 💜 by + the developers at Jam, using the open-source{" "} + + PapaParse + {" "} + package. +

+
+ +
+

How to Use Jam's JSON to TSV Converter Tool

+

+ Whether you're working on data analysis, creating reports, or + importing data into spreadsheet applications, our converter makes it + easy to convert your JSON files to TSV online. +

+ +

+ Need to convert the other way? You can use the TSV to JSON converter{" "} + here. +

+
+ +
+

Benefits of Converting JSON to TSV format

+

+ JSON is a flexible, easy-to-read data format used for storing + structured data. TSV (Tab-Separated Values) is a simple tabular format + widely supported by spreadsheet applications and data analysis tools. +

+ +
+ +
+

FAQs

+ +
+
+ ); +} diff --git a/components/seo/TsvToJsonSEO.tsx b/components/seo/TsvToJsonSEO.tsx new file mode 100644 index 0000000..de39153 --- /dev/null +++ b/components/seo/TsvToJsonSEO.tsx @@ -0,0 +1,138 @@ +import Link from "next/link"; + +export default function TsvToJsonSEO() { + return ( +
+
+

+ You can convert TSV files into JSON online with this free tool. If you + work with APIs, data, or web apps, you can use Jam's TSV to JSON + converter to turn tab-separated data into JSON format. +

+
+ +
+

+ Just paste your TSV file and get the JSON result. Built with 💜 by the + developers at Jam, using the open-source{" "} + + PapaParse + {" "} + package. +

+
+ +
+

How to Use Jam's TSV to JSON Converter Tool

+

+ Whether you're working on web development projects, data analysis, or + integrating with APIs, this converter makes it easy to convert TSV + files into JSON data. +

+ +

+ Need to convert the other way? You can use the JSON to TSV converter{" "} + here. +

+
+ +
+

More JSON Utilities

+

+ Beautify JSON, convert from query parameters, CSV, or YAML with Jam's + free developer utilities. They're all available in dark mode too. +

+ +
+ +
+

Benefits of Converting TSV to JSON format

+

+ TSV (Tab-Separated Values) is a simple file format used to store data + in tables, similar to CSV but using tabs as delimiters. JSON + (JavaScript Object Notation) is an easy-to-read data format that both + people and computers can understand. +

+ +
+ +
+

FAQs

+ +
+
+ ); +} diff --git a/components/utils/json-to-tsv.utils.test.ts b/components/utils/json-to-tsv.utils.test.ts new file mode 100644 index 0000000..80fe6db --- /dev/null +++ b/components/utils/json-to-tsv.utils.test.ts @@ -0,0 +1,63 @@ +import { convertJSONtoTSV } from "./json-to-tsv.utils"; + +describe("json-to-tsv.utils", () => { + // Helper function to normalize line endings + const normalizeTSV = (tsv: string) => tsv.replace(/\r\n/g, "\n").trim(); + + it("should convert a JSON string to TSV", () => { + const jsonString = '[{"name":"John","age":30},{"name":"Jane","age":25}]'; + const expectedTSV = "name\tage\nJohn\t30\nJane\t25"; + + expect(normalizeTSV(convertJSONtoTSV(jsonString))).toBe( + normalizeTSV(expectedTSV) + ); + }); + + it("should convert a JSON object to TSV", () => { + const jsonObject = { name: "John", age: 30 }; + const expectedTSV = "name\tage\nJohn\t30"; + + expect(normalizeTSV(convertJSONtoTSV(jsonObject))).toBe( + normalizeTSV(expectedTSV) + ); + }); + + it("should convert an array of JSON objects to TSV", () => { + const jsonArray = [ + { name: "John", age: 30 }, + { name: "Jane", age: 25 }, + ]; + const expectedTSV = "name\tage\nJohn\t30\nJane\t25"; + + expect(normalizeTSV(convertJSONtoTSV(jsonArray))).toBe( + normalizeTSV(expectedTSV) + ); + }); + + it("should handle empty input", () => { + expect(normalizeTSV(convertJSONtoTSV([]))).toBe(""); + expect(normalizeTSV(convertJSONtoTSV({}))).toBe(""); + expect(normalizeTSV(convertJSONtoTSV("[]"))).toBe(""); + expect(normalizeTSV(convertJSONtoTSV("{}"))).toBe(""); + }); + + it("should handle arrays within objects", () => { + const jsonArray = [ + { name: "John", hobbies: ["reading", "swimming"] }, + { name: "Jane", hobbies: ["painting"] }, + ]; + const expectedTSV = + "name\thobbies\nJohn\treading,swimming\nJane\tpainting"; + + expect(normalizeTSV(convertJSONtoTSV(jsonArray))).toBe( + normalizeTSV(expectedTSV) + ); + }); + + it("should throw an error for invalid input", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => convertJSONtoTSV(123 as any)).toThrow( + "Input must be a JSON string or an object." + ); + }); +}); diff --git a/components/utils/json-to-tsv.utils.ts b/components/utils/json-to-tsv.utils.ts new file mode 100644 index 0000000..4321a37 --- /dev/null +++ b/components/utils/json-to-tsv.utils.ts @@ -0,0 +1,35 @@ +import Papa from "papaparse"; + +export function convertJSONtoTSV(input: string | object): string { + try { + let data: object[] = []; + + if (typeof input === "string") { + data = JSON.parse(input); + } else if (Array.isArray(input)) { + data = input; + } else if (typeof input === "object") { + data = [input]; + } else { + throw new Error("Input must be a JSON string or an object."); + } + + const config = { + header: true, + delimiter: "\t", + newline: "\r\n", + quoteChar: '"', + escapeChar: '"', + skipEmptyLines: true, + }; + + const tsv = Papa.unparse(data, config); + return tsv; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("Failed to convert JSON to TSV: Unknown error"); + } + } +} diff --git a/components/utils/tools-list.ts b/components/utils/tools-list.ts index f234072..dc0a282 100644 --- a/components/utils/tools-list.ts +++ b/components/utils/tools-list.ts @@ -5,6 +5,12 @@ export const tools = [ "Easily convert CSV data to JSON format with our free tool. Quickest way to turn tabular data into a JSON format for APIs and data processing.", link: "/utilities/csv-to-json", }, + { + title: "TSV to JSON", + description: + "Easily convert TSV data to JSON format with our free tool. Quickest way to turn tab-separated data into a JSON format for APIs and data processing.", + link: "/utilities/tsv-to-json", + }, { title: "Base64 Encode/Decode", description: @@ -77,6 +83,12 @@ export const tools = [ "Transform your JSON data into sleek CSV format with Jam's free online converter. Simply paste your JSON and watch the magic happen!", link: "/utilities/json-to-csv", }, + { + title: "JSON to TSV", + description: + "Transform your JSON data into TSV format with Jam's free online converter. Simply paste your JSON and get tab-separated output instantly.", + link: "/utilities/json-to-tsv", + }, { title: "HAR file viewer", description: diff --git a/components/utils/tsv-to-json.utils.test.ts b/components/utils/tsv-to-json.utils.test.ts new file mode 100644 index 0000000..fe1adaf --- /dev/null +++ b/components/utils/tsv-to-json.utils.test.ts @@ -0,0 +1,107 @@ +import { convertTSVtoJSON } from "./tsv-to-json.utils"; + +describe("tsv-to-json.utils", () => { + it("should convert TSV to JSON with lowercase headers/keys", () => { + const tsv = ` + Name\tAge\tGender\tEmail\tPhone + John Doe\t35\tMale\tjohn.doe@example.com\t+1 (555) 123-4567 + Jane Smith\t28\tFemale\tjane.smith@example.com\t+1 (555) 987-6543 + Michael Johnson\t42\tMale\tmichael.johnson@example.com\t+1 (555) 456-7890 + Emily Brown\t31\tFemale\temily.brown@example.com\t+1 (555) 321-7654 + David Lee\t25\tMale\tdavid.lee@example.com\t+1 (555) 789-0123 +`; + const result = convertTSVtoJSON(tsv, true); + const expected = JSON.stringify( + [ + { + name: "John Doe", + age: "35", + gender: "Male", + email: "john.doe@example.com", + phone: "+1 (555) 123-4567", + }, + { + name: "Jane Smith", + age: "28", + gender: "Female", + email: "jane.smith@example.com", + phone: "+1 (555) 987-6543", + }, + { + name: "Michael Johnson", + age: "42", + gender: "Male", + email: "michael.johnson@example.com", + phone: "+1 (555) 456-7890", + }, + { + name: "Emily Brown", + age: "31", + gender: "Female", + email: "emily.brown@example.com", + phone: "+1 (555) 321-7654", + }, + { + name: "David Lee", + age: "25", + gender: "Male", + email: "david.lee@example.com", + phone: "+1 (555) 789-0123", + }, + ], + null, + 2 + ); + + expect(result).toBe(expected); + }); + + it("should convert TSV to JSON and trim values", () => { + const tsv = ` + Name\t Age\t Phone + John Doe\t 30\t +1 (555) 123-4567 + Jane Smith\t 25\t +1 (555) 987-6543 +`; + const result = convertTSVtoJSON(tsv, true); + const expected = JSON.stringify( + [ + { name: "John Doe", age: "30", phone: "+1 (555) 123-4567" }, + { name: "Jane Smith", age: "25", phone: "+1 (555) 987-6543" }, + ], + null, + 2 + ); + + expect(result).toBe(expected); + }); + + it("should skip empty lines in TSV", () => { + const tsv = ` + Name\t Age + + John Doe\t 30 + + Jane Smith\t 25 +`; + const result = convertTSVtoJSON(tsv, true); + const expected = JSON.stringify( + [ + { name: "John Doe", age: "30" }, + { name: "Jane Smith", age: "25" }, + ], + null, + 2 + ); + + expect(result).toBe(expected); + }); + + it("should preserve original case when lowercase is false", () => { + const tsv = `Name\tAge\nJohn\t25`; + const result = convertTSVtoJSON(tsv, false); + const parsed = JSON.parse(result); + + expect(parsed[0]).toHaveProperty("Name"); + expect(parsed[0]).toHaveProperty("Age"); + }); +}); diff --git a/components/utils/tsv-to-json.utils.ts b/components/utils/tsv-to-json.utils.ts new file mode 100644 index 0000000..f9a9f23 --- /dev/null +++ b/components/utils/tsv-to-json.utils.ts @@ -0,0 +1,21 @@ +import Papa from "papaparse"; + +export function convertTSVtoJSON(tsv: string, lowercase: boolean) { + const parsedData = Papa.parse(tsv, { + header: true, + skipEmptyLines: true, + delimiter: "\t", + transformHeader: (header) => { + return lowercase ? header.trim().toLowerCase() : header.trim(); + }, + transform: (value) => { + return value.trim(); + }, + }); + + if (parsedData.errors.length > 0) { + throw new Error(parsedData.errors[0].message); + } + + return JSON.stringify(parsedData.data, null, 2); +} diff --git a/pages/utilities/json-to-tsv.tsx b/pages/utilities/json-to-tsv.tsx new file mode 100644 index 0000000..5146334 --- /dev/null +++ b/pages/utilities/json-to-tsv.tsx @@ -0,0 +1,87 @@ +import { useCallback, useState } from "react"; +import { Textarea } from "@/components/ds/TextareaComponent"; +import PageHeader from "@/components/PageHeader"; +import { Card } from "@/components/ds/CardComponent"; +import { Button } from "@/components/ds/ButtonComponent"; +import { Label } from "@/components/ds/LabelComponent"; +import Header from "@/components/Header"; +import { CMDK } from "@/components/CMDK"; +import { useCopyToClipboard } from "@/components/hooks/useCopyToClipboard"; +import CallToActionGrid from "@/components/CallToActionGrid"; +import Meta from "@/components/Meta"; +import { convertJSONtoTSV } from "@/components/utils/json-to-tsv.utils"; +import JsonToTsvSEO from "@/components/seo/JsonToTsvSEO"; + +export default function JSONtoTSV() { + const [input, setInput] = useState(""); + const [output, setOutput] = useState(""); + const { buttonText, handleCopy } = useCopyToClipboard(); + + const handleChange = useCallback( + (event: React.ChangeEvent) => { + const value = event.currentTarget.value; + setInput(value); + + if (value.trim() === "") { + setOutput(""); + return; + } + + try { + const tsv = convertJSONtoTSV(value.trim()); + setOutput(tsv); + } catch (errorMessage: unknown) { + setOutput(errorMessage as string); + } + }, + [] + ); + + return ( +
+ +
+ + +
+ +
+ +
+ +
+ +