diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8a9074a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,63 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = crlf
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{cs,csx}]
+indent_size = 4
+dotnet_sort_system_directives_first = true
+dotnet_separate_import_directive_groups = false
+
+# C# code style
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_prefer_braces = true:suggestion
+csharp_style_var_for_built_in_types = false:suggestion
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_elsewhere = false:suggestion
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_properties = true:suggestion
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = file_scoped:suggestion
+
+# Naming conventions
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+dotnet_naming_style.underscore_camel_style.required_prefix = _
+dotnet_naming_style.underscore_camel_style.capitalization = camel_case
+
+dotnet_naming_rule.private_fields_should_be_underscore_camel.severity = suggestion
+dotnet_naming_rule.private_fields_should_be_underscore_camel.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be_underscore_camel.style = underscore_camel_style
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private
+
+[*.{xml,csproj,props,targets,config,nuspec}]
+indent_size = 2
+
+[*.{json,yaml,yml}]
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{sh,bash}]
+end_of_line = lf
+
+[Makefile]
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6ac8a2f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,45 @@
+# Auto detect text files and perform LF normalization
+* text=auto eol=crlf
+
+# Source files
+*.cs text eol=crlf diff=csharp
+*.vb text eol=crlf
+*.csproj text eol=crlf
+*.sln text eol=crlf
+*.props text eol=crlf
+*.targets text eol=crlf
+*.config text eol=crlf
+*.json text eol=crlf
+*.xml text eol=crlf
+*.yaml text eol=crlf
+*.yml text eol=crlf
+*.md text eol=crlf
+*.txt text eol=crlf
+*.csv text eol=crlf
+*.editorconfig text eol=crlf
+*.gitattributes text eol=crlf
+*.gitignore text eol=crlf
+
+# Shell scripts - keep LF
+*.sh text eol=lf
+*.bash text eol=lf
+
+# Binary files
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.ico binary
+*.pdf binary
+*.docx binary
+*.doc binary
+*.xls binary
+*.xlsx binary
+*.zip binary
+*.7z binary
+*.gz binary
+*.nupkg binary
+*.snupkg binary
+*.dll binary
+*.exe binary
+*.pdb binary
diff --git a/.github/WORKFLOWS.md b/.github/WORKFLOWS.md
new file mode 100644
index 0000000..a965634
--- /dev/null
+++ b/.github/WORKFLOWS.md
@@ -0,0 +1,83 @@
+# GitHub Actions Workflows
+
+This repository uses GitHub Actions for CI/CD automation.
+
+## Workflows
+
+### 1. **CI** (`ci.yml`)
+Runs on every push and pull request to `develop` and `master` branches.
+- ✅ Builds the solution (.NET 9)
+- ✅ Runs all unit tests
+- ✅ Collects code coverage
+- ✅ Uploads test results as artifacts
+
+### 2. **CodeQL Security Scan** (`codeql.yml`)
+Runs on push, pull request, and weekly schedule (Monday 03:00 UTC).
+- 🔒 Analyzes C# code for security vulnerabilities
+- 🔍 Detects code quality issues
+- 📊 Uploads results to GitHub Security tab
+
+### 3. **Publish to NuGet** (`nuget-publish.yml`)
+Automatically publishes to NuGet.org when a new release is created.
+- 📦 Extracts version from release tag (e.g., `v1.8.0`)
+- 🔨 Builds and tests the library
+- 📤 Publishes to NuGet.org
+- 💾 Saves package as artifact
+
+## Setup Instructions
+
+### Required Secrets
+
+To enable NuGet publishing, add the following secret to your repository:
+
+1. Go to **Settings** → **Secrets and variables** → **Actions**
+2. Click **New repository secret**
+3. Add:
+ - **Name:** `NUGET_API_KEY`
+ - **Value:** Your NuGet API key from https://www.nuget.org/account/apikeys
+
+### Creating a Release
+
+To publish a new version to NuGet:
+
+1. Update version in `src/FileConversionLibrary/FileConversionLibrary.csproj`
+2. Commit and push changes
+3. Create a new release on GitHub:
+ ```bash
+ git tag v1.8.0
+ git push origin v1.8.0
+ ```
+4. Go to GitHub → **Releases** → **Draft a new release**
+5. Choose the tag `v1.8.0`
+6. Add release notes
+7. Click **Publish release**
+
+The `nuget-publish.yml` workflow will automatically:
+- Extract version from the tag
+- Build and test the project
+- Publish to NuGet.org
+
+### Manual Publishing
+
+You can also trigger publishing manually:
+
+1. Go to **Actions** → **Publish to NuGet**
+2. Click **Run workflow**
+3. Enter version (e.g., `1.8.0`)
+4. Click **Run workflow**
+
+## Status Badges
+
+Add these to your README.md:
+
+```markdown
+[](https://github.com/TheMysteriousStranger90/FileConversionLibrary/actions/workflows/ci.yml)
+[](https://github.com/TheMysteriousStranger90/FileConversionLibrary/actions/workflows/codeql.yml)
+[](https://www.nuget.org/packages/FileConversionLibrary/)
+```
+
+## Requirements
+
+- **.NET 9.0** SDK
+- **Ubuntu latest** runner
+- **NuGet API Key** (for publishing)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..f75d58a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,65 @@
+name: CI
+
+on:
+ push:
+ branches: [ develop, master ]
+ pull_request:
+ branches: [ develop, master ]
+
+jobs:
+ build-and-test:
+ name: Build & Test (.NET 9)
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET 9
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore FileConversionLibrary.sln
+
+ - name: Build (Release)
+ run: dotnet build FileConversionLibrary.sln -c Release --no-restore
+
+ - name: Run unit tests
+ run: |
+ dotnet test FileConversionLibrary.sln \
+ -c Release \
+ --no-build \
+ --logger "trx;LogFileName=test-results.trx" \
+ --results-directory ./TestResults \
+ --collect:"XPlat Code Coverage" \
+ -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results
+ path: ./TestResults/*.trx
+ retention-days: 30
+
+ - name: Upload coverage reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-reports
+ path: ./TestResults/**/coverage.opencover.xml
+ retention-days: 30
+
+ - name: Build summary
+ if: always()
+ run: |
+ echo "### Build Summary :rocket:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **.NET Version:** 9.0" >> $GITHUB_STEP_SUMMARY
+ echo "- **Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..c2505b6
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,57 @@
+name: CodeQL Security Scan
+
+on:
+ push:
+ branches: [ develop, master ]
+ pull_request:
+ branches: [ develop, master ]
+ schedule:
+ # Run weekly on Monday at 03:00 UTC
+ - cron: '0 3 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze (C#)
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ permissions:
+ security-events: write
+ actions: read
+ contents: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET 9
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9.0.x'
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: csharp
+ queries: security-and-quality
+
+ - name: Restore dependencies
+ run: dotnet restore FileConversionLibrary.sln
+
+ - name: Build for CodeQL analysis
+ run: dotnet build FileConversionLibrary.sln -c Release --no-restore
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:csharp"
+
+ - name: Security scan summary
+ if: always()
+ run: |
+ echo "### Security Scan Summary :shield:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Language:** C#" >> $GITHUB_STEP_SUMMARY
+ echo "- **Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Scan Date:** $(date -u +''%Y-%m-%d %H:%M UTC'')" >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
deleted file mode 100644
index a1ad72b..0000000
--- a/.github/workflows/dotnet.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-# This workflow will build a .NET project
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
-
-name: .NET
-
-on:
- push:
- branches: [ "master" ]
- pull_request:
- branches: [ "master" ]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- outputs:
- Version: ${{ steps.gitversion.outputs.SemVer }}
- CommitsSinceVersionSource: ${{ steps.gitversion.outputs.CommitsSinceVersionSource }}
-
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0 # fetch-depth is needed for GitVersion
-
- # Install and calculate the new version with GitVersion
- - name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0.9.7
- with:
- versionSpec: 5.x
- - name: Determine Version
- uses: gittools/actions/gitversion/execute@v0.9.7
- id: gitversion # step id used as reference for output values
- - name: Display GitVersion outputs
- run: |
- echo "Version: ${{ steps.gitversion.outputs.SemVer }}"
- echo "CommitsSinceVersionSource: ${{ steps.gitversion.outputs.CommitsSinceVersionSource }}"
-
- # Build/pack the project
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 8.0.x
- - name: Restore dependencies
- run: dotnet restore FileConversionLibrary/FileConversionLibrary.csproj
- - name: Build the project
- run: dotnet build FileConversionLibrary/FileConversionLibrary.csproj -c Release
- - name: Pack the NuGet package
- run: dotnet pack FileConversionLibrary/FileConversionLibrary.csproj -p:Version='${{ steps.gitversion.outputs.SemVer }}' -c Release
- - name: Upload NuGet package as artifact
- uses: actions/upload-artifact@v4
- with:
- name: nugetPackage
- path: FileConversionLibrary/FileConversionLibrary/bin/Release/
diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml
new file mode 100644
index 0000000..9299114
--- /dev/null
+++ b/.github/workflows/nuget-publish.yml
@@ -0,0 +1,86 @@
+name: Publish to NuGet
+
+on:
+ release:
+ types: [published]
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version to publish (e.g., 1.8.0)'
+ required: true
+ type: string
+
+jobs:
+ publish:
+ name: Build and Publish to NuGet
+ runs-on: ubuntu-latest
+ environment: production
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET 9
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9.0.x'
+
+ - name: Extract version from tag
+ id: get_version
+ run: |
+ if [ "${{ github.event_name }}" = "release" ]; then
+ VERSION="${{ github.event.release.tag_name }}"
+ VERSION="${VERSION#v}"
+ else
+ VERSION="${{ github.event.inputs.version }}"
+ fi
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "Publishing version: $VERSION"
+
+ - name: Update project version
+ run: |
+ VERSION="${{ steps.get_version.outputs.version }}"
+ sed -i "s|.*|$VERSION|" src/FileConversionLibrary/FileConversionLibrary.csproj
+ sed -i "s|.*|$VERSION.0|" src/FileConversionLibrary/FileConversionLibrary.csproj
+ sed -i "s|.*|$VERSION.0|" src/FileConversionLibrary/FileConversionLibrary.csproj
+ echo "Updated version to $VERSION"
+
+ - name: Restore dependencies
+ run: dotnet restore FileConversionLibrary.sln
+
+ - name: Build (Release)
+ run: dotnet build FileConversionLibrary.sln -c Release --no-restore
+
+ - name: Run tests
+ run: dotnet test FileConversionLibrary.sln -c Release --no-build --verbosity normal
+
+ - name: Pack NuGet package
+ run: dotnet pack src/FileConversionLibrary/FileConversionLibrary.csproj -c Release --no-build -o ./artifacts
+
+ - name: Publish to NuGet.org
+ run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+
+ - name: Upload NuGet package artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-package-${{ steps.get_version.outputs.version }}
+ path: ./artifacts/*.nupkg
+ retention-days: 90
+
+ - name: Create release summary
+ run: |
+ echo "### NuGet Package Published :package:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Package:** FileConversionLibrary" >> $GITHUB_STEP_SUMMARY
+ echo "- **Version:** ${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **NuGet:** https://www.nuget.org/packages/FileConversionLibrary/${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "#### Installation" >> $GITHUB_STEP_SUMMARY
+ echo '```bash' >> $GITHUB_STEP_SUMMARY
+ echo "dotnet add package FileConversionLibrary --version ${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
diff --git a/.gitignore b/.gitignore
index 0c5ecd5..16549de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,49 @@
+## Build output
bin/
obj/
+out/
+
+## NuGet
/packages/
+*.nupkg
+*.snupkg
+nupkg/
+!**/assets/
+
+## IDE - Visual Studio
+.vs/
+*.user
+*.suo
+*.userosscache
+*.sln.docstates
+*.DotSettings.user
+
+## IDE - JetBrains Rider
+.idea/
+*.iml
riderModule.iml
/_ReSharper.Caches/
+*.DotSettings.user
-.idea/
-nupkg/
\ No newline at end of file
+## IDE - VS Code
+.vscode/
+!.vscode/extensions.json
+
+## OS
+.DS_Store
+Thumbs.db
+desktop.ini
+
+## Logs
+*.log
+
+## Test results
+TestResults/
+coverage/
+*.coverage
+*.coveragexml
+
+## Temp / scripts
+fix_*.py
+update_*.py
+*.bak
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..c8a2488
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,26 @@
+
+
+
+ net9.0
+ enable
+ enable
+ latest
+ true
+ true
+ latest-recommended
+ true
+ false
+
+
+
+
+ $(NoWarn);CA1303;CA1848;CA1002;NU1507
+
+
+
+
+ false
+ $(NoWarn);CA1303;CA1848;CA1062;CA2007;CA1707
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 0000000..e897852
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,25 @@
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FileConversionLibrary.Tests/CsvToJsonConverterTests.cs b/FileConversionLibrary.Tests/CsvToJsonConverterTests.cs
deleted file mode 100644
index b481090..0000000
--- a/FileConversionLibrary.Tests/CsvToJsonConverterTests.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-using Newtonsoft.Json.Linq;
-
-namespace FileConversionLibrary.Tests
-{
- public class CsvToJsonConverterTests
- {
- private CsvToJsonConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new CsvToJsonConverter();
- }
-
- [Test]
- public void Convert_GivenValidCsvData_ReturnsValidJson()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- Assert.IsNotNull(result);
-
- // Verify the JSON can be parsed
- var jsonArray = JArray.Parse(result);
- Assert.AreEqual(2, jsonArray.Count);
-
- // Verify the structure of the first object
- var firstObj = jsonArray[0];
- Assert.AreEqual("John", firstObj["Name"].ToString());
- Assert.AreEqual(30, firstObj["Age"].Value());
-
- // Verify the structure of the second object
- var secondObj = jsonArray[1];
- Assert.AreEqual("Jane", secondObj["Name"].ToString());
- Assert.AreEqual(25, secondObj["Age"].Value());
- }
-
- [Test]
- public void Convert_WithConvertValuesFalse_KeepsStrings()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["convertValues"] = false
- };
-
- // Act
- var result = _converter.Convert(csvData, options);
-
- // Assert
- var jsonObj = JArray.Parse(result)[0];
- Assert.AreEqual("30", jsonObj["Age"].ToString()); // Should remain a string
- }
-
- [Test]
- public void Convert_WithDifferentDataTypes_ConvertsCorrectly()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "String", "Integer", "Decimal", "Boolean" },
- Rows = new List
- {
- new[] { "text", "42", "3.14", "true" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- var jsonObj = JArray.Parse(result)[0];
- Assert.AreEqual("text", jsonObj["String"].ToString());
- Assert.AreEqual(42, jsonObj["Integer"].Value());
- Assert.AreEqual(3.14, jsonObj["Decimal"].Value());
- Assert.IsTrue(jsonObj["Boolean"].Value());
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/CsvToPdfConverterTests.cs b/FileConversionLibrary.Tests/CsvToPdfConverterTests.cs
deleted file mode 100644
index b86246f..0000000
--- a/FileConversionLibrary.Tests/CsvToPdfConverterTests.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-
-namespace FileConversionLibrary.Tests
-{
- public class CsvToPdfConverterTests
- {
- private CsvToPdfConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new CsvToPdfConverter();
- }
-
- [Test]
- public void Convert_GivenValidCsvData_ReturnsPdfBytes()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsInstanceOf(result);
- Assert.Greater(result.Length, 0);
-
- // Verify the PDF signature (%PDF-)
- byte[] pdfSignature = { 0x25, 0x50, 0x44, 0x46, 0x2D };
- Assert.IsTrue(StartsWithSequence(result, pdfSignature));
- }
-
- [Test]
- public void Convert_WithCustomOptions_ReturnsPdfBytes()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["fontSize"] = 12f,
- ["addBorders"] = false
- };
-
- // Act
- var result = _converter.Convert(csvData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
-
- // Helper method to check if a byte array starts with a sequence
- private bool StartsWithSequence(byte[] data, byte[] sequence)
- {
- if (data.Length < sequence.Length)
- return false;
-
- for (int i = 0; i < sequence.Length; i++)
- {
- if (data[i] != sequence[i])
- return false;
- }
-
- return true;
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/CsvToWordConverterTests.cs b/FileConversionLibrary.Tests/CsvToWordConverterTests.cs
deleted file mode 100644
index 54aee64..0000000
--- a/FileConversionLibrary.Tests/CsvToWordConverterTests.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-
-namespace FileConversionLibrary.Tests
-{
- public class CsvToWordConverterTests
- {
- private CsvToWordConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new CsvToWordConverter();
- }
-
- [Test]
- public void Convert_GivenValidCsvData_ReturnsWordBytes()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsInstanceOf(result);
- Assert.Greater(result.Length, 0);
-
- // Check for DOCX file signature (PK ZIP signature)
- byte[] docxSignature = { 0x50, 0x4B, 0x03, 0x04 };
- Assert.IsTrue(StartsWithSequence(result, docxSignature));
- }
-
- [Test]
- public void Convert_WithCustomOptions_ReturnsWordBytes()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["useTable"] = false,
- ["fontFamily"] = "Arial"
- };
-
- // Act
- var result = _converter.Convert(csvData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
-
- // Helper method to check if a byte array starts with a sequence
- private bool StartsWithSequence(byte[] data, byte[] sequence)
- {
- if (data.Length < sequence.Length)
- return false;
-
- for (int i = 0; i < sequence.Length; i++)
- {
- if (data[i] != sequence[i])
- return false;
- }
-
- return true;
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/CsvToXmlConverterTests.cs b/FileConversionLibrary.Tests/CsvToXmlConverterTests.cs
deleted file mode 100644
index 0cbae17..0000000
--- a/FileConversionLibrary.Tests/CsvToXmlConverterTests.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-
-namespace FileConversionLibrary.Tests
-{
- public class CsvToXmlConverterTests
- {
- private CsvToXmlConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new CsvToXmlConverter();
- }
-
- [Test]
- public void Convert_GivenValidCsvData_ReturnsValidXml()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- Assert.IsNotNull(result);
-
- // Verify the XML can be parsed
- var doc = XDocument.Parse(result);
- Assert.IsNotNull(doc.Root);
-
- // Verify structure
- var rows = doc.Root.Elements();
- Assert.AreEqual(2, rows.Count());
-
- // Check first row content
- var firstRow = rows.First();
- Assert.AreEqual("30", firstRow.Element("Age").Value);
- Assert.AreEqual("John", firstRow.Element("Name").Value);
- }
-
- [Test]
- public void Convert_WithElementsFormat_CreatesCorrectStructure()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["format"] = CsvToXmlConverter.XmlOutputFormat.Elements,
- ["useCData"] = false
- };
-
- // Act
- var result = _converter.Convert(csvData, options);
-
- // Assert
- var doc = XDocument.Parse(result);
- var firstRow = doc.Root.Elements().First();
-
- // Verify elements format (nested elements)
- Assert.IsNull(firstRow.Attribute("Name"));
- Assert.IsNotNull(firstRow.Element("Name"));
- }
-
- [Test]
- public void Convert_WithAttributesFormat_CreatesCorrectStructure()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["format"] = CsvToXmlConverter.XmlOutputFormat.Attributes
- };
-
- // Act
- var result = _converter.Convert(csvData, options);
-
- // Assert
- var doc = XDocument.Parse(result);
- var firstRow = doc.Root.Elements().First();
-
- // Verify attributes format
- Assert.IsNotNull(firstRow.Attribute("Name"));
- Assert.AreEqual("John", firstRow.Attribute("Name").Value);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/CsvToYamlConverterTests.cs b/FileConversionLibrary.Tests/CsvToYamlConverterTests.cs
deleted file mode 100644
index 2830333..0000000
--- a/FileConversionLibrary.Tests/CsvToYamlConverterTests.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-using YamlDotNet.RepresentationModel;
-
-namespace FileConversionLibrary.Tests
-{
- public class CsvToYamlConverterTests
- {
- private CsvToYamlConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new CsvToYamlConverter();
- }
-
- [Test]
- public void Convert_GivenValidCsvData_ReturnsValidYaml()
- {
- // Arrange
- var csvData = new CsvData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(csvData);
-
- // Assert
- Assert.IsNotNull(result);
-
- // Verify the YAML can be parsed
- using var stringReader = new StringReader(result);
- var yamlStream = new YamlStream();
- yamlStream.Load(stringReader);
-
- var document = yamlStream.Documents[0];
- var rootNode = document.RootNode as YamlSequenceNode;
-
- Assert.IsNotNull(rootNode);
- Assert.AreEqual(2, rootNode.Children.Count);
-
- // Verify first item structure
- var firstItem = rootNode.Children[0] as YamlMappingNode;
- Assert.IsTrue(firstItem.Children.ContainsKey(new YamlScalarNode("Name")));
- Assert.AreEqual("John",
- ((YamlScalarNode)firstItem.Children[new YamlScalarNode("Name")]).Value);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/FileConversionLibrary.Tests.csproj b/FileConversionLibrary.Tests/FileConversionLibrary.Tests.csproj
deleted file mode 100644
index 0674e4d..0000000
--- a/FileConversionLibrary.Tests/FileConversionLibrary.Tests.csproj
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
- false
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/FileConversionLibrary.Tests/GlobalUsings.cs b/FileConversionLibrary.Tests/GlobalUsings.cs
deleted file mode 100644
index cefced4..0000000
--- a/FileConversionLibrary.Tests/GlobalUsings.cs
+++ /dev/null
@@ -1 +0,0 @@
-global using NUnit.Framework;
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/XmlToCsvConverterTests.cs b/FileConversionLibrary.Tests/XmlToCsvConverterTests.cs
deleted file mode 100644
index 6a458fa..0000000
--- a/FileConversionLibrary.Tests/XmlToCsvConverterTests.cs
+++ /dev/null
@@ -1,276 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-
-namespace FileConversionLibrary.Tests
-{
- [TestFixture]
- public class XmlToCsvConverterTests
- {
- private XmlToCsvConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new XmlToCsvConverter();
- }
-
- [Test]
- public void Convert_GivenValidXmlData_ReturnsValidCsv()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- var lines = result.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
-
- Assert.IsTrue(lines[0].Contains("Age") && lines[0].Contains("Name"));
-
- Assert.IsTrue(result.Contains("John") && result.Contains("30"));
- Assert.IsTrue(result.Contains("Jane") && result.Contains("25"));
- }
-
- [Test]
- public void Convert_WithSemicolonDelimiter_UsesSemicolon()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["delimiter"] = ';'
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsTrue(result.Contains("Age;Name"));
- Assert.IsTrue(result.Contains("30;John"));
- }
-
- [Test]
- public void Convert_WithQuoteValues_AddsQuotes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John, Doe
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["quoteValues"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsTrue(result.Contains("\"John, Doe\""));
- }
-
- [Test]
- public void Convert_WithDataContainingSpecialChars_EscapesCorrectly()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- Line 1
-Line 2""quoted""
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["quoteValues"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsTrue(result.Contains("\"Line 1\nLine 2\"\"quoted\"\"\""));
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
-
- [Test]
- public void Convert_WithEmptyDocument_ThrowsArgumentException()
- {
- // Arrange
- var xmlData = new XmlData
- {
- Document = new XDocument()
- };
-
- // Act & Assert
- Assert.Throws(() => _converter.Convert(xmlData));
- }
-
- [Test]
- public void Convert_WithTabularDataOnly_UsesTabularData()
- {
- // Arrange
- var xmlData = new XmlData
- {
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsTrue(result.Contains("Name,Age"));
- Assert.IsTrue(result.Contains("John,30"));
- Assert.IsTrue(result.Contains("Jane,25"));
- }
-
- [Test]
- public void Convert_WithAttributes_IncludesAttributes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsTrue(result.Contains("@id") && result.Contains("@status"));
- Assert.IsTrue(result.Contains("1") && result.Contains("active"));
- }
-
- [Test]
- public void Convert_WithFlattenHierarchyDisabled_DoesNotFlatten()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
-
- John
- 30
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["flattenHierarchy"] = false
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsTrue(result.Contains("profile"));
- }
-
- public void Convert_WithCustomNullValue_UsesCustomNull()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
-
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["customNullValue"] = "N/A"
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Console.WriteLine("Actual result:");
- Console.WriteLine(result);
-
- Assert.IsNotNull(result);
-
- var lines = result.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- bool foundCustomNull = lines.Any(line => line.Contains("N/A"));
-
- Assert.IsTrue(foundCustomNull, $"Expected to find 'N/A' in result. Actual result:\n{result}");
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/XmlToJsonConverterTests.cs b/FileConversionLibrary.Tests/XmlToJsonConverterTests.cs
deleted file mode 100644
index c1d8069..0000000
--- a/FileConversionLibrary.Tests/XmlToJsonConverterTests.cs
+++ /dev/null
@@ -1,202 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-using Newtonsoft.Json.Linq;
-
-namespace FileConversionLibrary.Tests
-{
- [TestFixture]
- public class XmlToJsonConverterTests
- {
- private XmlToJsonConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new XmlToJsonConverter();
- }
-
- [Test]
- public void Convert_GivenValidXmlData_ReturnsValidJson()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
-
- // Verify the JSON can be parsed
- var jsonObj = JObject.Parse(result);
- Assert.IsNotNull(jsonObj["root"]);
-
- var elements = jsonObj["root"]["element"];
- Assert.AreEqual(2, elements.Count());
-
- Assert.AreEqual("John", elements[0]["Name"].ToString());
- Assert.AreEqual("30", elements[0]["Age"].ToString());
- }
-
- [Test]
- public void Convert_WithNestedElements_PreservesStructure()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
-
- 30
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- var jsonObj = JObject.Parse(result);
- var details = jsonObj["root"]["element"]["Details"];
- Assert.IsNotNull(details);
- Assert.AreEqual("30", details["Age"].ToString());
- }
-
- [Test]
- public void Convert_WithAttributes_IncludesAttributes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- var jsonObj = JObject.Parse(result);
- var elements = jsonObj["root"]["element"];
-
- Assert.AreEqual("John", elements[0]["@Name"].ToString());
- Assert.AreEqual("30", elements[0]["@Age"].ToString());
- }
-
- [Test]
- public void Convert_WithConvertValuesOption_ConvertsTypes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
- true
- 9.5
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["convertValues"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- var jsonObj = JObject.Parse(result);
- var element = jsonObj["root"]["element"];
-
- // Check that Age is a number, not string
- Assert.IsInstanceOf(element["Age"]);
- Assert.AreEqual(JTokenType.Integer, element["Age"].Type);
- Assert.AreEqual(30, element["Age"].Value());
-
- // Check that Active is a boolean
- Assert.IsInstanceOf(element["Active"]);
- Assert.AreEqual(JTokenType.Boolean, element["Active"].Type);
- Assert.AreEqual(true, element["Active"].Value());
-
- // Check that Score is a decimal
- Assert.IsInstanceOf(element["Score"]);
- Assert.AreEqual(JTokenType.Float, element["Score"].Type);
- Assert.AreEqual(9.5, element["Score"].Value());
- }
-
- [Test]
- public void Convert_WithRemoveWhitespaceOption_RemovesEmptyTextNodes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
-
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc
- };
-
- var options = new Dictionary
- {
- ["removeWhitespace"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- var jsonObj = JObject.Parse(result);
- var element = jsonObj["root"]["element"];
-
- // Description should still exist but not have #text node
- Assert.IsNotNull(element["Description"]);
- Assert.IsFalse(element["Description"].ToString().Contains("#text"));
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/XmlToPdfConverterTests.cs b/FileConversionLibrary.Tests/XmlToPdfConverterTests.cs
deleted file mode 100644
index 4c0dfef..0000000
--- a/FileConversionLibrary.Tests/XmlToPdfConverterTests.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-using iTextSharp.text;
-
-namespace FileConversionLibrary.Tests
-{
- [TestFixture]
- public class XmlToPdfConverterTests
- {
- private XmlToPdfConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new XmlToPdfConverter();
- }
-
- [Test]
- public void Convert_GivenValidXmlData_ReturnsPdfBytes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsInstanceOf(result);
- Assert.Greater(result.Length, 0);
-
- // Verify PDF header signature
- byte[] pdfSignature = { 0x25, 0x50, 0x44, 0x46 }; // %PDF
- for (int i = 0; i < pdfSignature.Length; i++)
- {
- Assert.AreEqual(pdfSignature[i], result[i]);
- }
- }
-
- [Test]
- public void Convert_WithCustomFontSize_ReturnsPdf()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["fontSize"] = 12f
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithHierarchicalView_ReturnsPdf()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
-
- 30
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Details.Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["hierarchicalView"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithAlternateRowColors_ReturnsPdf()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- var options = new Dictionary
- {
- ["alternateRowColors"] = true,
- ["headerBackgroundColor"] = new BaseColor(200, 200, 200)
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/XmlToWordConverterTests.cs b/FileConversionLibrary.Tests/XmlToWordConverterTests.cs
deleted file mode 100644
index 00375cf..0000000
--- a/FileConversionLibrary.Tests/XmlToWordConverterTests.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-
-namespace FileConversionLibrary.Tests
-{
- [TestFixture]
- public class XmlToWordConverterTests
- {
- private XmlToWordConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new XmlToWordConverter();
- }
-
- [Test]
- public void Convert_GivenValidXmlData_ReturnsWordBytes()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.IsInstanceOf(result);
- Assert.Greater(result.Length, 0);
-
- // Check for DOCX file signature (PK ZIP signature)
- byte[] docxSignature = { 0x50, 0x4B, 0x03, 0x04 };
- for (int i = 0; i < docxSignature.Length; i++)
- {
- Assert.AreEqual(docxSignature[i], result[i]);
- }
- }
-
- [Test]
- public void Convert_WithoutTable_ReturnsWordDocument()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["useTable"] = false
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithHierarchicalFormat_ReturnsWordDocument()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
-
- 30
-
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Details.Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["formatAsHierarchy"] = true
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithCustomFontSettings_ReturnsWordDocument()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" }
- }
- };
-
- var options = new Dictionary
- {
- ["fontFamily"] = "Arial",
- ["fontSize"] = 14
- };
-
- // Act
- var result = _converter.Convert(xmlData, options);
-
- // Assert
- Assert.IsNotNull(result);
- Assert.Greater(result.Length, 0);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.Tests/XmlToYamlConverterTests.cs b/FileConversionLibrary.Tests/XmlToYamlConverterTests.cs
deleted file mode 100644
index 9d31b68..0000000
--- a/FileConversionLibrary.Tests/XmlToYamlConverterTests.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary.Converters;
-using FileConversionLibrary.Models;
-using YamlDotNet.RepresentationModel;
-
-namespace FileConversionLibrary.Tests
-{
- [TestFixture]
- public class XmlToYamlConverterTests
- {
- private XmlToYamlConverter _converter;
-
- [SetUp]
- public void SetUp()
- {
- _converter = new XmlToYamlConverter();
- }
-
- [Test]
- public void Convert_GivenValidXmlData_ReturnsValidYaml()
- {
- // Arrange
- var xmlDoc = XDocument.Parse(@"
-
- John
- 30
-
-
- Jane
- 25
-
- ");
-
- var xmlData = new XmlData
- {
- Document = xmlDoc,
- Headers = new[] { "Name", "Age" },
- Rows = new List
- {
- new[] { "John", "30" },
- new[] { "Jane", "25" }
- }
- };
-
- // Act
- var result = _converter.Convert(xmlData);
-
- // Assert
- Assert.IsNotNull(result);
-
- // Verify the YAML can be parsed
- using var reader = new StringReader(result);
- var yamlStream = new YamlStream();
- yamlStream.Load(reader);
-
- // At least one document should be present
- Assert.GreaterOrEqual(yamlStream.Documents.Count, 1);
- Assert.IsNotNull(yamlStream.Documents[0].RootNode);
- }
-
- [Test]
- public void Convert_WithNullInput_ThrowsArgumentException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => _converter.Convert(null));
- }
- }
-}
\ No newline at end of file
diff --git a/FileConversionLibrary.sln b/FileConversionLibrary.sln
index d690967..09da685 100644
--- a/FileConversionLibrary.sln
+++ b/FileConversionLibrary.sln
@@ -1,28 +1,78 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibrary", "FileConversionLibrary\FileConversionLibrary.csproj", "{834D918D-1FE6-4954-B29E-E578A3D75850}"
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1A1A1A1-A1A1-A1A1-A1A1-A1A1A1A1A1A1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibrary.Tests", "FileConversionLibrary.Tests\FileConversionLibrary.Tests.csproj", "{AD889C7E-9909-4DF4-82F0-F6741B16F043}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{C3C3C3C3-C3C3-C3C3-C3C3-C3C3C3C3C3C3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibraryConsoleExample", "FileConversionLibraryConsoleExample\FileConversionLibraryConsoleExample.csproj", "{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibraryConsoleExample", "samples\FileConversionLibraryConsoleExample\FileConversionLibraryConsoleExample.csproj", "{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibrary", "src\FileConversionLibrary\FileConversionLibrary.csproj", "{E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D9A44480-71ED-4501-A3C3-C75DE2082D66}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
+ Directory.Packages.props = Directory.Packages.props
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConversionLibrary.Tests", "tests\FileConversionLibrary.Tests\FileConversionLibrary.Tests.csproj", "{E973AD6D-7910-45B0-8516-5DB2A6C239F5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {834D918D-1FE6-4954-B29E-E578A3D75850}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {834D918D-1FE6-4954-B29E-E578A3D75850}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {834D918D-1FE6-4954-B29E-E578A3D75850}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {834D918D-1FE6-4954-B29E-E578A3D75850}.Release|Any CPU.Build.0 = Release|Any CPU
- {AD889C7E-9909-4DF4-82F0-F6741B16F043}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AD889C7E-9909-4DF4-82F0-F6741B16F043}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AD889C7E-9909-4DF4-82F0-F6741B16F043}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AD889C7E-9909-4DF4-82F0-F6741B16F043}.Release|Any CPU.Build.0 = Release|Any CPU
{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|x64.Build.0 = Debug|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Debug|x86.Build.0 = Debug|Any CPU
{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|x64.ActiveCfg = Release|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|x64.Build.0 = Release|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|x86.ActiveCfg = Release|Any CPU
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB}.Release|x86.Build.0 = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|x64.Build.0 = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Debug|x86.Build.0 = Debug|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|x64.ActiveCfg = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|x64.Build.0 = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|x86.ActiveCfg = Release|Any CPU
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5}.Release|x86.Build.0 = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|x64.Build.0 = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Debug|x86.Build.0 = Debug|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|x64.ActiveCfg = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|x64.Build.0 = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|x86.ActiveCfg = Release|Any CPU
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {40D4EEC9-D867-40B8-AD80-69B2C085B5CB} = {C3C3C3C3-C3C3-C3C3-C3C3-C3C3C3C3C3C3}
+ {E5E5E5E5-E5E5-E5E5-E5E5-E5E5E5E5E5E5} = {A1A1A1A1-A1A1-A1A1-A1A1-A1A1A1A1A1A1}
+ {E973AD6D-7910-45B0-8516-5DB2A6C239F5} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
diff --git a/FileConversionLibrary.sln.DotSettings.user b/FileConversionLibrary.sln.DotSettings.user
index fb29774..b786381 100644
--- a/FileConversionLibrary.sln.DotSettings.user
+++ b/FileConversionLibrary.sln.DotSettings.user
@@ -1,4 +1,4 @@
<SessionState ContinuousTestingMode="0" IsActive="True" Name="CsvToXmlConverterTests" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
- <Project Location="D:\MyProjects\FileConversionLibrary\FileConversionLibrary.Tests" Presentation="<FileConversionLibrary.Tests>" />
+ <Project Location="D:\MyProjects\FileConversionLibrary" Presentation="<tests>" />
</SessionState>
\ No newline at end of file
diff --git a/FileConversionLibrary/FileConversionLibrary.csproj b/FileConversionLibrary/FileConversionLibrary.csproj
deleted file mode 100644
index e6c443a..0000000
--- a/FileConversionLibrary/FileConversionLibrary.csproj
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
- net8.0
- enable
- enable
- true
- FileConversionLibrary
- 1.7.0
- FileConversionLibrary
- Bohdan Harabadzhyu
- FileConversionLibrary is a .NET library for converting files between different formats.
- It supports converting CSV files to XML, PDF, Word, JSON, and YAML formats, as well as converting XML files to CSV, PDF, Word, JSON, and YAML formats.
- The library provides asynchronous methods for all conversions and includes comprehensive error handling.
- https://github.com/TheMysteriousStranger90/FileConversionLibrary
- git
- https://github.com/TheMysteriousStranger90/FileConversionLibrary
- file-conversion, csv, xml
- Version 1.7.0:
- - (Released: 2 September 2025) - Bug fixes and improved XML parsing reliability
- - Fixed a critical bug in XML parsing logic that incorrectly identified data rows in complex, nested XML files, significantly improving accuracy.
- - Enhanced the heuristic for identifying repeating data records in XML to make it more robust for various document structures.
- - Corrected an issue in the XML-to-CSV converter to ensure it uses the correctly parsed data, preventing inconsistent output.
- - Fixed a data loss bug in the XML-to-JSON converter where child elements were being deleted when a CDATA section was present.
- - General stability and reliability improvements for all XML conversion pathways.
- - Simplified the `IFileReader` interface to a single, more flexible `ReadAsync` method.
- - Refactored `CsvFileReader` and `XmlFileReader` to align with the new unified API, improving internal consistency and maintainability.
- - **API Unification:** Standardized all file-based conversion methods in `FileConverter` to accept strongly-typed options classes (e.g., `JsonConversionOptions`, `PdfConversionOptions`) instead of long parameter lists. This creates a consistent, predictable, and extensible API across all conversion types (file, stream, and in-memory).
-
- Version 1.6.0:
- - (Released: 6 August 2025) - Enhanced XML processing capabilities
- - Added robust support for complex XML structures with nested elements
- - Implemented proper handling of CDATA sections across all XML converters
- - Added support for XML comments in output formats
- - Enhanced handling of XML attributes and hierarchical structures
- - Improved error handling for malformed XML documents
- - Optimized performance for large XML documents with complex structures
- - Added new configuration options for XML conversion customization
-
- Version 1.5.0:
- - (Released: 4 August 2025) - Major update with new APIs and enhanced functionality.
- - Added Stream API for direct stream-to-stream conversions without temporary files (Currently In Testing Mode).
- - Introduced In-Memory conversion API for working with data objects directly (Currently In Testing Mode).
- - Added comprehensive strongly-typed options classes for all conversion types.
- - Enhanced performance and memory efficiency for large dataset processing.
- - Added support for web applications and microservices scenarios.
- - Implemented advanced configuration options for all output formats.
-
- true
- MIT
- README.md
- Cover.png
-
- FileConversionLibrary
- Advanced file format conversion library for .NET
- Bohdan Harabadzhyu
- FileConversionLibrary
- Copyright © 2025 Bohdan Harabadzhyu
- 1.7.0.0
- 1.7.0.0
-
- true
- latest
- false
-
-
- true
- portable
- true
-
- latest
- strict
-
-
-
-
-
-
-
-
-
-
-
-
- True
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/FileConversionLibrary/Models/CsvData.cs b/FileConversionLibrary/Models/CsvData.cs
deleted file mode 100644
index 1880633..0000000
--- a/FileConversionLibrary/Models/CsvData.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace FileConversionLibrary.Models;
-
-public class CsvData
-{
- [Required]
- public string[] Headers { get; set; } = Array.Empty();
-
- [Required]
- public List Rows { get; set; } = new List();
-}
\ No newline at end of file
diff --git a/FileConversionLibrary/Models/XmlData.cs b/FileConversionLibrary/Models/XmlData.cs
deleted file mode 100644
index 33008eb..0000000
--- a/FileConversionLibrary/Models/XmlData.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.Xml.Linq;
-
-namespace FileConversionLibrary.Models;
-
-public class XmlData
-{
- public XDocument Document { get; set; } = new XDocument();
-
- [Obsolete("Consider using Document property for better XML handling. This property is maintained for backward compatibility.")]
- public string[] Headers { get; set; } = Array.Empty();
-
- [Obsolete("Consider using Document property for better XML handling. This property is maintained for backward compatibility.")]
- public List Rows { get; set; } = new List();
-
- [Required]
- public string RootElementName { get; set; } = string.Empty;
-
- [Required]
- public string XmlVersion { get; set; } = "1.0";
-
- [Required]
- public string Encoding { get; set; } = "UTF-8";
-}
\ No newline at end of file
diff --git a/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj b/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj
deleted file mode 100644
index 5f8ba1f..0000000
--- a/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- Exe
- net8.0
- enable
- enable
- false
- false
-
-
-
-
-
-
-
diff --git a/FileConversionLibraryConsoleExample/Program.cs b/FileConversionLibraryConsoleExample/Program.cs
deleted file mode 100644
index 5187320..0000000
--- a/FileConversionLibraryConsoleExample/Program.cs
+++ /dev/null
@@ -1,331 +0,0 @@
-using System.Xml.Linq;
-using FileConversionLibrary;
-using FileConversionLibrary.Models;
-using FileConversionLibrary.Models.Options;
-
-namespace FileConversionLibraryConsoleExample;
-
-///
-/// This console application serves as a demonstration and testing ground
-/// for the FileConversionLibrary, showcasing its various conversion capabilities.
-///
-class Program
-{
- ///
- /// The main entry point for the application.
- /// Initializes the FileConverter and runs a series of conversion tests.
- ///
- static async Task Main(string[] args)
- {
- var fileConverter = new FileConverter();
-
- Console.WriteLine("=== FileConversionLibrary Test Console ===\n");
-
- try
- {
- // Run tests for the file-based conversion API.
- await TestFileBasedAPI(fileConverter);
-
- // Uncomment to run tests for the Stream-based conversion API.
- //await TestStreamAPI(fileConverter);
-
- // Uncomment to run tests for the In-Memory conversion API.
- //TestInMemoryAPI(fileConverter);
-
- Console.WriteLine("\n🎉 All tests completed successfully!");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"\n❌ An error occurred: {ex.Message}");
- Console.WriteLine($"Details: {ex}");
- }
-
- Console.WriteLine("\nPress any key to exit...");
- Console.ReadKey();
- }
-
- ///
- /// Tests the primary file-to-file conversion methods of the library.
- /// This demonstrates reading from a source file and writing to a destination file
- /// using the new unified API with strongly-typed options classes.
- ///
- static async Task TestFileBasedAPI(FileConverter fileConverter)
- {
- Console.WriteLine("📁 Testing File-based API:");
- Console.WriteLine("===========================");
-
- // --- CSV to Other Formats ---
- await fileConverter.ConvertCsvToPdfAsync(
- @"C:\Users\User\Desktop\csv_input.csv", @"C:\Users\User\Desktop\output1.pdf");
- Console.WriteLine("✅ CSV to PDF conversion completed.");
-
- await fileConverter.ConvertCsvToJsonAsync(
- @"C:\Users\User\Desktop\csv_input.csv",
- @"C:\Users\User\Desktop\output1.json"
- );
- Console.WriteLine("✅ CSV to JSON conversion completed.");
-
- await fileConverter.ConvertCsvToWordAsync(
- @"C:\Users\User\Desktop\csv_input.csv",
- @"C:\Users\User\Desktop\output1.docx"
- );
- Console.WriteLine("✅ CSV to Word conversion completed.");
-
- await fileConverter.ConvertCsvToXmlAsync(
- @"C:\Users\User\Desktop\csv_input.csv",
- @"C:\Users\User\Desktop\output1.xml"
- );
- Console.WriteLine("✅ CSV to XML conversion completed.");
-
- await fileConverter.ConvertCsvToYamlAsync(
- @"C:\Users\User\Desktop\csv_input.csv",
- @"C:\Users\User\Desktop\output1.yaml"
- );
- Console.WriteLine("✅ CSV to YAML conversion completed.");
-
- // --- XML to Other Formats ---
- await fileConverter.ConvertXmlToCsvAsync(
- @"C:\Users\User\Desktop\xml_input.xml",
- @"C:\Users\User\Desktop\output2.csv"
- );
- Console.WriteLine("✅ XML to CSV conversion completed.");
-
- await fileConverter.ConvertXmlToJsonAsync(
- @"C:\Users\User\Desktop\xml_input.xml",
- @"C:\Users\User\Desktop\output2.json"
- );
- Console.WriteLine("✅ XML to JSON conversion completed.");
-
- await fileConverter.ConvertXmlToPdfAsync(
- @"C:\Users\User\Desktop\xml_input.xml",
- @"C:\Users\User\Desktop\output2.pdf"
- );
- Console.WriteLine("✅ XML to PDF conversion completed.");
-
- await fileConverter.ConvertXmlToWordAsync(
- @"C:\Users\User\Desktop\xml_input.xml",
- @"C:\Users\User\Desktop\output2.docx");
- Console.WriteLine("✅ XML to Word conversion completed.");
-
- await fileConverter.ConvertXmlToYamlAsync(
- @"C:\Users\User\Desktop\xml_input.xml",
- @"C:\Users\User\Desktop\output2.yaml");
- Console.WriteLine("✅ XML to YAML conversion completed.");
-
- Console.WriteLine();
- }
-
- ///
- /// Tests the stream-based conversion API, which is ideal for web applications
- /// or scenarios where data is processed without being saved to disk.
- ///
- static async Task TestStreamAPI(FileConverter fileConverter)
- {
- Console.WriteLine("🌊 Testing New Stream API:");
- Console.WriteLine("==========================");
-
- // Test Stream API with CSV to JSON
- if (File.Exists(@"C:\Users\User\Desktop\csv_input.csv"))
- {
- using var inputStream = File.OpenRead(@"C:\Users\User\Desktop\csv_input.csv");
-
- // Test ConvertStreamToStringAsync: Reads a stream and returns the converted content as a string.
- var jsonOptions = new JsonConversionOptions
- {
- SourceFormat = "csv",
- TargetFormat = "json",
- UseIndentation = true
- };
-
- var jsonResult = await fileConverter.ConvertStreamToStringAsync(inputStream, jsonOptions);
- await File.WriteAllTextAsync(@"C:\Users\User\Desktop\stream_output.json", jsonResult);
- Console.WriteLine("✅ Stream to JSON string conversion completed.");
-
- // Reset stream position for the next test
- inputStream.Position = 0;
-
- // Test ConvertStreamToBytesAsync: Reads a stream and returns the converted content as a byte array (e.g., for PDF/Word).
- var pdfOptions = new PdfConversionOptions
- {
- SourceFormat = "csv",
- TargetFormat = "pdf",
- Title = "Streamed PDF Report"
- };
-
- var pdfBytes = await fileConverter.ConvertStreamToBytesAsync(inputStream, pdfOptions);
- await File.WriteAllBytesAsync(@"C:\Users\User\Desktop\stream_output.pdf", pdfBytes);
- Console.WriteLine("✅ Stream to PDF bytes conversion completed.");
-
- // Reset stream position for the next test
- inputStream.Position = 0;
-
- // Test ConvertStreamAsync (Stream-to-Stream): Reads an input stream and returns a new output stream.
- var xmlOptions = new XmlConversionOptions
- {
- SourceFormat = "csv",
- TargetFormat = "xml"
- };
-
- using var outputStream = await fileConverter.ConvertStreamAsync(inputStream, xmlOptions);
- using var fileStream = File.Create(@"C:\Users\User\Desktop\stream_output.xml");
- await outputStream.CopyToAsync(fileStream);
- Console.WriteLine("✅ Stream to Stream conversion completed.");
- }
- else
- {
- Console.WriteLine("⚠️ CSV input file not found, skipping Stream API tests.");
- }
-
- Console.WriteLine();
- }
-
- ///
- /// Tests the in-memory conversion API, where data is represented by C# objects
- /// (like CsvData or XmlData) instead of files.
- ///
- static void TestInMemoryAPI(FileConverter fileConverter)
- {
- Console.WriteLine("💾 Testing New In-Memory API:");
- Console.WriteLine("=============================");
-
- // Create sample CsvData object in memory.
- var csvData = new CsvData
- {
- Headers = new[] { "ID", "Name", "Age", "City", "Salary", "Department" },
- Rows = new List
- {
- new[] { "1", "John Doe", "28", "New York", "75000", "Engineering" },
- new[] { "2", "Jane Smith", "32", "London", "82000", "Marketing" },
- new[] { "3", "Bob Johnson", "45", "Tokyo", "95000", "Sales" },
- new[] { "4", "Alice Brown", "29", "Berlin", "68000", "HR" },
- new[] { "5", "Charlie Wilson", "35", "Sydney", "71000", "Finance" }
- }
- };
-
- // Test advanced JSON conversion with options
- var jsonOptions = new JsonConversionOptions
- {
- ConvertValues = true,
- UseIndentation = true
- };
- var json = fileConverter.ConvertCsvToJson(csvData, jsonOptions);
- File.WriteAllText(@"C:\Users\User\Desktop\inmemory_output.json", json);
- Console.WriteLine("✅ In-Memory CSV to JSON conversion completed.");
-
- // Test advanced PDF conversion with options
- var pdfOptions = new PdfConversionOptions
- {
- FontSize = 11f,
- Title = "Employee Report",
- AlternateRowColors = true
- };
- var pdfBytes = fileConverter.ConvertCsvToPdf(csvData, pdfOptions);
- File.WriteAllBytes(@"C:\Users\User\Desktop\inmemory_output.pdf", pdfBytes);
- Console.WriteLine("✅ In-Memory CSV to PDF conversion completed.");
-
- // Test advanced Word conversion with options
- var wordOptions = new WordConversionOptions
- {
- UseTable = true,
- FontFamily = "Calibri",
- FontSize = 11,
- AlternateRowColors = true
- };
- var wordBytes = fileConverter.ConvertCsvToWord(csvData, wordOptions);
- File.WriteAllBytes(@"C:\Users\User\Desktop\inmemory_output.docx", wordBytes);
- Console.WriteLine("✅ In-Memory CSV to Word conversion completed.");
-
- // Test advanced XML conversion with options
- var xmlOptions = new XmlConversionOptions
- {
- OutputFormat = "Elements",
- UseCData = false,
- AddComments = true
- };
- var xml = fileConverter.ConvertCsvToXml(csvData, xmlOptions);
- File.WriteAllText(@"C:\Users\User\Desktop\inmemory_output.xml", xml);
- Console.WriteLine("✅ In-Memory CSV to XML conversion completed.");
-
- // Test advanced YAML conversion with options
- var yamlOptions = new YamlConversionOptions
- {
- Structure = "Array",
- NamingConvention = "CamelCase",
- ConvertDataTypes = true
- };
- var yaml = fileConverter.ConvertCsvToYaml(csvData, yamlOptions);
- File.WriteAllText(@"C:\Users\User\Desktop\inmemory_output.yaml", yaml);
- Console.WriteLine("✅ In-Memory CSV to YAML conversion completed.");
-
- // Test in-memory conversions starting from XML data.
- TestXmlInMemoryConversions(fileConverter);
-
- Console.WriteLine();
- }
-
- ///
- /// Tests in-memory conversions that start with XML data.
- /// It demonstrates the two ways to provide XML data: as a pre-parsed table (Headers/Rows)
- /// or as a full XML document (XDocument).
- ///
- static void TestXmlInMemoryConversions(FileConverter fileConverter)
- {
- Console.WriteLine("\n🔄 Testing XML In-Memory Conversions:");
-
- try
- {
- // For in-memory conversions to table-based formats (PDF, Word, CSV),
- // we manually provide the headers and rows, as if they were already parsed from an XML file.
- // This is the expected input for these converters.
- var xmlDataForTables = new XmlData
- {
- Headers = new[] { "ProductID", "ProductName", "Price", "Category", "InStock" },
- Rows = new List
- {
- new[] { "1", "Laptop Pro", "1299.99", "Electronics", "true" },
- new[] { "2", "Office Chair", "249.50", "Furniture", "false" },
- new[] { "3", "Programming Book", "45.99", "Books", "true" },
- new[] { "4", "Wireless Mouse", "29.99", "Electronics", "true" }
- },
- RootElementName = "Products"
- };
-
- // For conversions that work with the full XML structure (JSON, YAML),
- // we provide the XDocument object directly.
- var xmlContent = @"1Laptop Pro1299.99Electronicstrue2Office Chair249.50Furniturefalse";
- var xmlDataForTree = new XmlData
- {
- Document = XDocument.Parse(xmlContent)
- };
-
- // XML to CSV (uses the table-based data)
- var csv = fileConverter.ConvertXmlToCsv(xmlDataForTables, new CsvConversionOptions { Delimiter = ',' });
- File.WriteAllText(@"C:\Users\User\Desktop\xml_inmemory_output.csv", csv);
- Console.WriteLine("✅ In-Memory XML to CSV conversion completed.");
-
- // XML to JSON (uses the tree-based data)
- var json = fileConverter.ConvertXmlToJson(xmlDataForTree, new JsonConversionOptions { ConvertValues = true, UseIndentation = true });
- File.WriteAllText(@"C:\Users\User\Desktop\xml_inmemory_output.json", json);
- Console.WriteLine("✅ In-Memory XML to JSON conversion completed.");
-
- // XML to PDF (uses the table-based data)
- var pdfBytes = fileConverter.ConvertXmlToPdf(xmlDataForTables, new PdfConversionOptions { Title = "Product Catalog", AlternateRowColors = true });
- File.WriteAllBytes(@"C:\Users\User\Desktop\xml_inmemory_output.pdf", pdfBytes);
- Console.WriteLine("✅ In-Memory XML to PDF conversion completed.");
-
- // XML to Word (uses the table-based data)
- var wordBytes = fileConverter.ConvertXmlToWord(xmlDataForTables, new WordConversionOptions { UseTable = true, FontFamily = "Arial" });
- File.WriteAllBytes(@"C:\Users\User\Desktop\xml_inmemory_output.docx", wordBytes);
- Console.WriteLine("✅ In-Memory XML to Word conversion completed.");
-
- // XML to YAML (uses the tree-based data)
- var yaml = fileConverter.ConvertXmlToYaml(xmlDataForTree, new YamlConversionOptions { ConvertDataTypes = true });
- File.WriteAllText(@"C:\Users\User\Desktop\xml_inmemory_output.yaml", yaml);
- Console.WriteLine("✅ In-Memory XML to YAML conversion completed.");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"❌ Error in XML In-Memory conversions: {ex.Message}");
- }
- }
-}
\ No newline at end of file
diff --git a/README.md b/README.md
index fe32493..1d5ea33 100644
--- a/README.md
+++ b/README.md
@@ -2,23 +2,26 @@

-A .NET library for converting CSV and XML files to various formats including XML, PDF, Word, JSON, and YAML. Now with enhanced Stream API and In-Memory conversion capabilities!
+A .NET library for converting CSV and XML files to various formats including XML, PDF, Word, JSON, and YAML. Now with
+enhanced Stream API and In-Memory conversion capabilities!
## Key Features
-- **Unified API**: A consistent and predictable API across file, stream, and in-memory conversions using strongly-typed options classes.
-- **Robust Parsing**: Advanced heuristics to reliably parse complex and real-world CSV and XML files.
-- **Multiple Conversion Modes**:
- - **File-Based**: Convert files directly from disk.
- - **Stream-Based**: For web applications, microservices, and data pipelines.
- - **In-Memory**: High-performance, low-overhead conversions on data objects.
-- **Extensive Customization**: Fine-tune every aspect of the output with detailed, format-specific options.
+- **Unified API**: A consistent and predictable API across file, stream, and in-memory conversions using strongly-typed
+ options classes.
+- **Robust Parsing**: Advanced heuristics to reliably parse complex and real-world CSV and XML files.
+- **Multiple Conversion Modes**:
+ - **File-Based**: Convert files directly from disk.
+ - **Stream-Based**: For web applications, microservices, and data pipelines.
+ - **In-Memory**: High-performance, low-overhead conversions on data objects.
+- **Extensive Customization**: Fine-tune every aspect of the output with detailed, format-specific options.
## Usage
### 1. File-Based Conversion
-The most straightforward way to use the library. All methods now accept a dedicated options class for easy configuration.
+The most straightforward way to use the library. All methods now accept a dedicated options class for easy
+configuration.
```csharp
// Create a single instance of the converter
@@ -94,8 +97,8 @@ File.WriteAllBytes("in_memory_report.docx", wordBytes);
The library supports two ways to provide in-memory XML data:
-- **For Table-Based Formats (CSV, PDF, Word):** Provide pre-parsed `Headers` and `Rows`.
-- **For Tree-Based Formats (JSON, YAML):** Provide the full `XDocument`.
+- **For Table-Based Formats (CSV, PDF, Word):** Provide pre-parsed `Headers` and `Rows`.
+- **For Tree-Based Formats (JSON, YAML):** Provide the full `XDocument`.
```csharp
// --- For Table-Based output (e.g., PDF) ---
@@ -122,12 +125,12 @@ var json = fileConverter.ConvertXmlToJson(xmlDataForTree, new JsonConversionOpti
Customize your output by passing an options object to any conversion method.
-- **`JsonConversionOptions`**: Control indentation, data type conversion, object nesting, and more.
-- **`PdfConversionOptions`**: Set titles, font sizes, page orientation, and row styling.
-- **`WordConversionOptions`**: Generate a table or hierarchical text, set fonts, and style rows.
-- **`XmlConversionOptions`**: Define output structure (elements vs. attributes), naming conventions, and metadata.
-- **`YamlConversionOptions`**: Choose data structure, naming conventions, and data type handling.
-- **`CsvConversionOptions`**: Specify delimiters, quoting behavior, and attribute handling for XML sources.
+- **`JsonConversionOptions`**: Control indentation, data type conversion, object nesting, and more.
+- **`PdfConversionOptions`**: Set titles, font sizes, page orientation, and row styling.
+- **`WordConversionOptions`**: Generate a table or hierarchical text, set fonts, and style rows.
+- **`XmlConversionOptions`**: Define output structure (elements vs. attributes), naming conventions, and metadata.
+- **`YamlConversionOptions`**: Choose data structure, naming conventions, and data type handling.
+- **`CsvConversionOptions`**: Specify delimiters, quoting behavior, and attribute handling for XML sources.
## Contributing
@@ -139,4 +142,4 @@ Bohdan Harabadzhyu
## License
-This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
\ No newline at end of file
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/FileConversionLibrary/Cover.png b/assets/Cover.png
similarity index 100%
rename from FileConversionLibrary/Cover.png
rename to assets/Cover.png
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..4e800bf
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj b/samples/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj
new file mode 100644
index 0000000..8034395
--- /dev/null
+++ b/samples/FileConversionLibraryConsoleExample/FileConversionLibraryConsoleExample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ false
+
+ $(NoWarn);CS0618;CA1861
+
+
+
+
+
+
+
diff --git a/samples/FileConversionLibraryConsoleExample/Program.cs b/samples/FileConversionLibraryConsoleExample/Program.cs
new file mode 100644
index 0000000..7ba6232
--- /dev/null
+++ b/samples/FileConversionLibraryConsoleExample/Program.cs
@@ -0,0 +1,353 @@
+using System.Text;
+using System.Xml.Linq;
+using FileConversionLibrary;
+using FileConversionLibrary.Models;
+using FileConversionLibrary.Models.Options;
+
+namespace FileConversionLibraryConsoleExample;
+
+///
+/// Demonstrates the capabilities of FileConversionLibrary with comprehensive examples
+/// of file, stream, and in-memory conversion APIs.
+///
+sealed class Program
+{
+ private static readonly FileConverter _converter = new();
+ private static readonly string _desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+
+ static async Task Main(string[] args)
+ {
+ Console.OutputEncoding = Encoding.UTF8;
+ Console.WriteLine("╔═══════════════════════════════════════════════════════════╗");
+ Console.WriteLine("║ FileConversionLibrary - Demonstration Console ║");
+ Console.WriteLine("║ .NET 9 | v1.8.0 ║");
+ Console.WriteLine("╚═══════════════════════════════════════════════════════════╝\n");
+
+ try
+ {
+ await RunInteractiveDemo();
+
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("\n✓ All demonstrations completed successfully!");
+ Console.ResetColor();
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"\n✗ Error: {ex.Message}");
+ Console.ResetColor();
+
+ if (args.Contains("--verbose"))
+ {
+ Console.WriteLine($"\nStack trace:\n{ex.StackTrace}");
+ }
+ }
+
+ Console.WriteLine("\nPress any key to exit...");
+ Console.ReadKey();
+ }
+
+ static async Task RunInteractiveDemo()
+ {
+ while (true)
+ {
+ DisplayMenu();
+ var choice = Console.ReadLine()?.Trim();
+
+ switch (choice)
+ {
+ case "1":
+ await DemoFileConversions();
+ break;
+ case "2":
+ await DemoStreamConversions();
+ break;
+ case "3":
+ DemoInMemoryConversions();
+ break;
+ case "4":
+ await DemoAdvancedOptions();
+ break;
+ case "5":
+ return;
+ default:
+ Console.WriteLine("Invalid choice. Please try again.\n");
+ break;
+ }
+ }
+ }
+
+ static void DisplayMenu()
+ {
+ Console.WriteLine("\n┌─────────────────────────────────────────┐");
+ Console.WriteLine("│ Select Demo Type │");
+ Console.WriteLine("├─────────────────────────────────────────┤");
+ Console.WriteLine("│ 1. File-to-File Conversions │");
+ Console.WriteLine("│ 2. Stream-based Conversions │");
+ Console.WriteLine("│ 3. In-Memory Conversions │");
+ Console.WriteLine("│ 4. Advanced Options Examples │");
+ Console.WriteLine("│ 5. Exit │");
+ Console.WriteLine("└─────────────────────────────────────────┘");
+ Console.Write("\nYour choice: ");
+ }
+
+ static async Task DemoFileConversions()
+ {
+ Console.WriteLine("\n📁 File-to-File Conversions\n" + new string('─', 50));
+
+ var csvPath = Path.Combine(_desktopPath, "sample.csv");
+ var xmlPath = Path.Combine(_desktopPath, "sample.xml");
+
+ // Create sample files if they don't exist
+ await EnsureSampleFiles(csvPath, xmlPath);
+
+ // CSV Conversions
+ await ConvertWithLog("CSV → JSON", () =>
+ _converter.ConvertCsvToJsonAsync(csvPath, Path.Combine(_desktopPath, "output-from-csv.json")));
+
+ await ConvertWithLog("CSV → PDF", () =>
+ _converter.ConvertCsvToPdfAsync(csvPath, Path.Combine(_desktopPath, "output-from-csv.pdf")));
+
+ await ConvertWithLog("CSV → Word", () =>
+ _converter.ConvertCsvToWordAsync(csvPath, Path.Combine(_desktopPath, "output-from-csv.docx")));
+
+ await ConvertWithLog("CSV → XML", () =>
+ _converter.ConvertCsvToXmlAsync(csvPath, Path.Combine(_desktopPath, "output-from-csv.xml")));
+
+ await ConvertWithLog("CSV → YAML", () =>
+ _converter.ConvertCsvToYamlAsync(csvPath, Path.Combine(_desktopPath, "output-from-csv.yaml")));
+
+ // XML Conversions
+ await ConvertWithLog("XML → CSV", () =>
+ _converter.ConvertXmlToCsvAsync(xmlPath, Path.Combine(_desktopPath, "output-from-xml.csv")));
+
+ await ConvertWithLog("XML → JSON", () =>
+ _converter.ConvertXmlToJsonAsync(xmlPath, Path.Combine(_desktopPath, "output-from-xml.json")));
+
+ await ConvertWithLog("XML → PDF", () =>
+ _converter.ConvertXmlToPdfAsync(xmlPath, Path.Combine(_desktopPath, "output-from-xml.pdf")));
+
+ await ConvertWithLog("XML → Word", () =>
+ _converter.ConvertXmlToWordAsync(xmlPath, Path.Combine(_desktopPath, "output-from-xml.docx")));
+
+ await ConvertWithLog("XML → YAML", () =>
+ _converter.ConvertXmlToYamlAsync(xmlPath, Path.Combine(_desktopPath, "output-from-xml.yaml")));
+
+ Console.WriteLine($"\n✓ All files saved to: {_desktopPath}");
+ }
+
+ static async Task DemoStreamConversions()
+ {
+ Console.WriteLine("\n🌊 Stream-based Conversions\n" + new string('─', 50));
+
+ var csvPath = Path.Combine(_desktopPath, "sample.csv");
+ await EnsureSampleFiles(csvPath, "");
+
+ // Stream to String (for text formats: JSON, YAML, XML)
+ using (var inputStream = File.OpenRead(csvPath))
+ {
+ var jsonOptions = new JsonConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "json",
+ UseIndentation = true
+ };
+
+ var jsonResult = await _converter.ConvertStreamToStringAsync(inputStream, jsonOptions);
+ await File.WriteAllTextAsync(Path.Combine(_desktopPath, "stream-output.json"), jsonResult);
+ Console.WriteLine("✓ CSV → JSON (via stream)");
+ }
+
+ // Stream to Bytes (for binary formats: PDF, Word)
+ using (var inputStream = File.OpenRead(csvPath))
+ {
+ var pdfOptions = new PdfConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "pdf",
+ Title = "Stream Demo Report"
+ };
+
+ var pdfBytes = await _converter.ConvertStreamToBytesAsync(inputStream, pdfOptions);
+ await File.WriteAllBytesAsync(Path.Combine(_desktopPath, "stream-output.pdf"), pdfBytes);
+ Console.WriteLine("✓ CSV → PDF (via stream)");
+ }
+
+ // Stream to Stream
+ using (var inputStream = File.OpenRead(csvPath))
+ {
+ var xmlOptions = new XmlConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "xml",
+ UseCData = true
+ };
+
+ using var outputStream = await _converter.ConvertStreamAsync(inputStream, xmlOptions);
+ using var fileStream = File.Create(Path.Combine(_desktopPath, "stream-output.xml"));
+ await outputStream.CopyToAsync(fileStream);
+ Console.WriteLine("✓ CSV → XML (stream-to-stream)");
+ }
+ }
+
+ static void DemoInMemoryConversions()
+ {
+ Console.WriteLine("\n💾 In-Memory Conversions\n" + new string('─', 50));
+
+ // Create sample data in memory
+ var csvData = new CsvData
+ {
+ Headers = ["ID", "Product", "Price", "Stock"],
+ Rows =
+ [
+ ["1", "Laptop", "1299.99", "15"],
+ ["2", "Mouse", "29.99", "150"],
+ ["3", "Keyboard", "79.99", "85"]
+ ]
+ };
+
+ // Convert CsvData to JSON
+ var jsonResult = _converter.ConvertCsvToJson(csvData);
+ File.WriteAllText(Path.Combine(_desktopPath, "memory-output.json"), jsonResult);
+ Console.WriteLine("✓ CsvData → JSON");
+
+ // Convert CsvData to XML
+ var xmlResult = _converter.ConvertCsvToXml(csvData);
+ File.WriteAllText(Path.Combine(_desktopPath, "memory-output.xml"), xmlResult);
+ Console.WriteLine("✓ CsvData → XML");
+
+ // Create XmlData in memory
+ var xmlDoc = XDocument.Parse(@"
+
+
+ Monitor
+ 299.99
+
+
+ Webcam
+ 89.99
+
+ ");
+
+ var xmlData = new XmlData { Document = xmlDoc };
+
+ // Convert XmlData to JSON
+ var xmlToJsonResult = _converter.ConvertXmlToJson(xmlData);
+ File.WriteAllText(Path.Combine(_desktopPath, "memory-xml-output.json"), xmlToJsonResult);
+ Console.WriteLine("✓ XmlData → JSON");
+
+ // Convert XmlData to CSV
+ var xmlToCsvResult = _converter.ConvertXmlToCsv(xmlData);
+ File.WriteAllText(Path.Combine(_desktopPath, "memory-xml-output.csv"), xmlToCsvResult);
+ Console.WriteLine("✓ XmlData → CSV");
+ }
+
+ static async Task DemoAdvancedOptions()
+ {
+ Console.WriteLine("\n⚙️ Advanced Options Examples\n" + new string('─', 50));
+
+ var csvPath = Path.Combine(_desktopPath, "sample.csv");
+ await EnsureSampleFiles(csvPath, "");
+
+ // JSON with custom options
+ var jsonOptions = new JsonConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "json",
+ UseIndentation = true,
+ ConvertValues = true
+ };
+ using (var input = File.OpenRead(csvPath))
+ {
+ var result = await _converter.ConvertStreamToStringAsync(input, jsonOptions);
+ await File.WriteAllTextAsync(Path.Combine(_desktopPath, "advanced-json.json"), result);
+ }
+
+ Console.WriteLine("✓ JSON with type conversion and indentation");
+
+ // PDF with custom options
+ var pdfOptions = new PdfConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "pdf",
+ Title = "Advanced PDF Report",
+ FontSize = 12,
+ LandscapeOrientation = true
+ };
+ using (var input = File.OpenRead(csvPath))
+ {
+ input.Position = 0;
+ var result = await _converter.ConvertStreamToBytesAsync(input, pdfOptions);
+ await File.WriteAllBytesAsync(Path.Combine(_desktopPath, "advanced.pdf"), result);
+ }
+
+ Console.WriteLine("✓ PDF with landscape orientation and custom font");
+
+ // Word with custom options
+ var wordOptions = new WordConversionOptions
+ {
+ SourceFormat = "csv",
+ TargetFormat = "word",
+ UseTable = true,
+ AddHeaderRow = true
+ };
+ using (var input = File.OpenRead(csvPath))
+ {
+ input.Position = 0;
+ var result = await _converter.ConvertStreamToBytesAsync(input, wordOptions);
+ await File.WriteAllBytesAsync(Path.Combine(_desktopPath, "advanced.docx"), result);
+ }
+
+ Console.WriteLine("✓ Word document with table format");
+ }
+
+ static async Task EnsureSampleFiles(string csvPath, string xmlPath)
+ {
+ // Create sample CSV
+ if (!File.Exists(csvPath))
+ {
+ var csvContent = @"ID,Name,Age,City,Department,Salary
+1,John Doe,28,New York,Engineering,75000
+2,Jane Smith,32,London,Marketing,82000
+3,Bob Johnson,45,Tokyo,Sales,95000
+4,Alice Brown,29,Berlin,HR,68000";
+ await File.WriteAllTextAsync(csvPath, csvContent);
+ }
+
+ // Create sample XML
+ if (!string.IsNullOrEmpty(xmlPath) && !File.Exists(xmlPath))
+ {
+ var xmlContent = @"
+
+
+ John Doe
+ 28
+ New York
+ Engineering
+ 75000
+
+
+ Jane Smith
+ 32
+ London
+ Marketing
+ 82000
+
+";
+ await File.WriteAllTextAsync(xmlPath, xmlContent);
+ }
+ }
+
+ static async Task ConvertWithLog(string description, Func conversionAction)
+ {
+ try
+ {
+ await conversionAction();
+ Console.WriteLine($"✓ {description}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"✗ {description} - Error: {ex.Message}");
+ }
+ }
+}
diff --git a/FileConversionLibrary/Converters/CsvToJsonConverter.cs b/src/FileConversionLibrary/Converters/CsvToJsonConverter.cs
similarity index 85%
rename from FileConversionLibrary/Converters/CsvToJsonConverter.cs
rename to src/FileConversionLibrary/Converters/CsvToJsonConverter.cs
index 993b966..073dfd9 100644
--- a/FileConversionLibrary/Converters/CsvToJsonConverter.cs
+++ b/src/FileConversionLibrary/Converters/CsvToJsonConverter.cs
@@ -1,4 +1,4 @@
-using FileConversionLibrary.Interfaces;
+using FileConversionLibrary.Interfaces;
using FileConversionLibrary.Models;
using Newtonsoft.Json;
using System.Globalization;
@@ -73,7 +73,8 @@ public string Convert(CsvData input, object? options = null)
preserveEmptyValues = preserveValue;
}
- if (optionsDict.TryGetValue("dateFormat", out var dateFormatObj) && dateFormatObj is string dateFormatValue)
+ if (optionsDict.TryGetValue("dateFormat", out var dateFormatObj) &&
+ dateFormatObj is string dateFormatValue)
{
dateFormat = dateFormatValue;
}
@@ -91,8 +92,9 @@ public string Convert(CsvData input, object? options = null)
if (groupByColumn && !string.IsNullOrEmpty(groupByColumnName))
{
- return ConvertGrouped(input, groupByColumnName, convertValues, useIndentation,
- preserveEmptyValues, dateFormat, convertArrays, arrayDelimiter, createNestedObjects, nestedSeparator);
+ return ConvertGrouped(input, groupByColumnName, convertValues, useIndentation,
+ preserveEmptyValues, dateFormat, convertArrays, arrayDelimiter, createNestedObjects,
+ nestedSeparator);
}
var resultList = new List>();
@@ -119,7 +121,8 @@ public string Convert(CsvData input, object? options = null)
var processedValue = ProcessValue(value, convertValues, dateFormat, convertArrays, arrayDelimiter);
- if (createNestedObjects && header.Contains(nestedSeparator))
+ if (createNestedObjects && nestedSeparator != null &&
+ header.Contains(nestedSeparator, StringComparison.Ordinal))
{
CreateNestedObject(rowDict, header, processedValue, nestedSeparator);
}
@@ -142,8 +145,8 @@ public string Convert(CsvData input, object? options = null)
return JsonConvert.SerializeObject(resultList, jsonSettings);
}
- private string ConvertGrouped(CsvData input, string groupByColumnName, bool convertValues,
- bool useIndentation, bool preserveEmptyValues, string? dateFormat, bool convertArrays,
+ private static string ConvertGrouped(CsvData input, string groupByColumnName, bool convertValues,
+ bool useIndentation, bool preserveEmptyValues, string? dateFormat, bool convertArrays,
string? arrayDelimiter, bool createNestedObjects, string? nestedSeparator)
{
var groupColumnIndex = Array.IndexOf(input.Headers, groupByColumnName);
@@ -159,9 +162,10 @@ private string ConvertGrouped(CsvData input, string groupByColumnName, bool conv
if (groupColumnIndex >= row.Length) continue;
var groupValue = row[groupColumnIndex] ?? "null";
- if (!groups.ContainsKey(groupValue))
+ if (!groups.TryGetValue(groupValue, out var groupList))
{
- groups[groupValue] = new List>();
+ groupList = new List>();
+ groups[groupValue] = groupList;
}
var rowDict = new Dictionary();
@@ -179,7 +183,8 @@ private string ConvertGrouped(CsvData input, string groupByColumnName, bool conv
var processedValue = ProcessValue(value, convertValues, dateFormat, convertArrays, arrayDelimiter);
- if (createNestedObjects && header.Contains(nestedSeparator))
+ if (createNestedObjects && nestedSeparator != null &&
+ header.Contains(nestedSeparator, StringComparison.Ordinal))
{
CreateNestedObject(rowDict, header, processedValue, nestedSeparator);
}
@@ -189,7 +194,7 @@ private string ConvertGrouped(CsvData input, string groupByColumnName, bool conv
}
}
- groups[groupValue].Add(rowDict);
+ groupList.Add(rowDict);
}
var jsonSettings = new JsonSerializerSettings
@@ -201,7 +206,7 @@ private string ConvertGrouped(CsvData input, string groupByColumnName, bool conv
return JsonConvert.SerializeObject(groups, jsonSettings);
}
- private object ProcessValue(string? value, bool convertValues, string? dateFormat,
+ private static object ProcessValue(string? value, bool convertValues, string? dateFormat,
bool convertArrays, string? arrayDelimiter)
{
if (string.IsNullOrEmpty(value))
@@ -218,15 +223,15 @@ private object ProcessValue(string? value, bool convertValues, string? dateForma
return ProcessSingleValue(value, convertValues, dateFormat);
}
- private object ProcessSingleValue(string value, bool convertValues, string? dateFormat)
+ private static object ProcessSingleValue(string value, bool convertValues, string? dateFormat)
{
if (!convertValues)
return value;
if (!string.IsNullOrEmpty(dateFormat))
{
- if (DateTime.TryParseExact(value, dateFormat, CultureInfo.InvariantCulture,
- DateTimeStyles.None, out var dateValue))
+ if (DateTime.TryParseExact(value, dateFormat, CultureInfo.InvariantCulture,
+ DateTimeStyles.None, out var dateValue))
{
return dateValue;
}
@@ -265,7 +270,8 @@ private object ProcessSingleValue(string value, bool convertValues, string? date
return value;
}
- private void CreateNestedObject(Dictionary target, string key, object value, string separator)
+ private static void CreateNestedObject(Dictionary target, string key, object value,
+ string separator)
{
var parts = key.Split(separator, StringSplitOptions.RemoveEmptyEntries);
var current = target;
@@ -273,12 +279,13 @@ private void CreateNestedObject(Dictionary target, string key, o
for (int i = 0; i < parts.Length - 1; i++)
{
var part = parts[i];
- if (!current.ContainsKey(part))
+ if (!current.TryGetValue(part, out var existingValue))
{
- current[part] = new Dictionary();
+ existingValue = new Dictionary();
+ current[part] = existingValue;
}
- if (current[part] is Dictionary dict)
+ if (existingValue is Dictionary dict)
{
current = dict;
}
@@ -292,4 +299,4 @@ private void CreateNestedObject(Dictionary target, string key, o
current[parts[^1]] = value;
}
}
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/CsvToPdfConverter.cs b/src/FileConversionLibrary/Converters/CsvToPdfConverter.cs
similarity index 92%
rename from FileConversionLibrary/Converters/CsvToPdfConverter.cs
rename to src/FileConversionLibrary/Converters/CsvToPdfConverter.cs
index 7a5bbce..ea221cd 100644
--- a/FileConversionLibrary/Converters/CsvToPdfConverter.cs
+++ b/src/FileConversionLibrary/Converters/CsvToPdfConverter.cs
@@ -1,4 +1,4 @@
-using FileConversionLibrary.Interfaces;
+using FileConversionLibrary.Interfaces;
using FileConversionLibrary.Models;
using iTextSharp.text;
using iTextSharp.text.pdf;
@@ -194,7 +194,9 @@ public byte[] Convert(CsvData input, object? options = null)
if (includeRowNumbers)
{
- var rowNumberCell = new PdfPCell(new Phrase((i + 1).ToString(), cellFont));
+ var rowNumberCell =
+ new PdfPCell(new Phrase((i + 1).ToString(System.Globalization.CultureInfo.InvariantCulture),
+ cellFont));
if (rowBackground != null)
{
rowNumberCell.BackgroundColor = rowBackground;
@@ -218,7 +220,7 @@ public byte[] Convert(CsvData input, object? options = null)
if (cellText.Length > 200)
{
- cellText = cellText.Substring(0, 197) + "...";
+ cellText = string.Concat(cellText.AsSpan(0, 197), "...");
}
var cell = new PdfPCell(new Phrase(cellText, cellFont));
@@ -242,13 +244,22 @@ public byte[] Convert(CsvData input, object? options = null)
}
document.Add(table);
+
+ if (input.Rows.Count == 0)
+ {
+ var noDataParagraph = new Paragraph("No data available.", cellFont);
+ noDataParagraph.Alignment = Element.ALIGN_CENTER;
+ noDataParagraph.SpacingBefore = 10f;
+ document.Add(noDataParagraph);
+ }
+
document.Close();
return memoryStream.ToArray();
}
}
- private float[] CalculateColumnWidths(CsvData input, bool includeRowNumbers)
+ private static float[] CalculateColumnWidths(CsvData input, bool includeRowNumbers)
{
var columnCount = input.Headers.Length + (includeRowNumbers ? 1 : 0);
var widths = new float[columnCount];
@@ -278,4 +289,4 @@ private float[] CalculateColumnWidths(CsvData input, bool includeRowNumbers)
return widths;
}
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/CsvToWordConverter.cs b/src/FileConversionLibrary/Converters/CsvToWordConverter.cs
similarity index 84%
rename from FileConversionLibrary/Converters/CsvToWordConverter.cs
rename to src/FileConversionLibrary/Converters/CsvToWordConverter.cs
index ba5d1e1..d50f7f3 100644
--- a/FileConversionLibrary/Converters/CsvToWordConverter.cs
+++ b/src/FileConversionLibrary/Converters/CsvToWordConverter.cs
@@ -110,7 +110,8 @@ public byte[] Convert(CsvData input, object? options = null)
includeStatistics = statsValue;
}
- if (optionsDict.TryGetValue("pageOrientation", out var orientation) && orientation is string orientationValue)
+ if (optionsDict.TryGetValue("pageOrientation", out var orientation) &&
+ orientation is string orientationValue)
{
pageOrientation = orientationValue;
}
@@ -154,8 +155,8 @@ public byte[] Convert(CsvData input, object? options = null)
}
else
{
- AddTableContent(body, input, fontFamily, fontSize, addHeaderRow,
- includeRowNumbers, alternateRowColors, addBorders, autoFitTable,
+ AddTableContent(body, input, fontFamily, fontSize, addHeaderRow,
+ includeRowNumbers, alternateRowColors, addBorders, autoFitTable,
headerStyle, tableWidth, wrapText);
}
}
@@ -176,12 +177,12 @@ public byte[] Convert(CsvData input, object? options = null)
}
}
- private void SetupPageSettings(Body body, string orientation)
+ private static void SetupPageSettings(Body body, string orientation)
{
var sectionProperties = new SectionProperties();
var pageSize = new PageSize();
- if (orientation.ToLower() == "landscape")
+ if (orientation.Equals("landscape", StringComparison.OrdinalIgnoreCase))
{
pageSize.Width = 15840U;
pageSize.Height = 12240U;
@@ -198,14 +199,14 @@ private void SetupPageSettings(Body body, string orientation)
body.Append(sectionProperties);
}
- private void AddTitle(Body body, string title, string fontFamily, int titleFontSize)
+ private static void AddTitle(Body body, string title, string fontFamily, int titleFontSize)
{
var titleParagraph = new Paragraph();
var titleRun = new Run();
var titleText = new Text(title);
var titleRunProperties = new RunProperties(
- new FontSize { Val = (titleFontSize * 2).ToString() },
+ new FontSize { Val = (titleFontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Bold(),
new RunFonts { Ascii = fontFamily }
);
@@ -223,14 +224,14 @@ private void AddTitle(Body body, string title, string fontFamily, int titleFontS
body.AppendChild(titleParagraph);
}
- private void AddTimestamp(Body body, string fontFamily, int fontSize)
+ private static void AddTimestamp(Body body, string fontFamily, int fontSize)
{
var timestampParagraph = new Paragraph();
var timestampRun = new Run();
var timestampText = new Text($"Generated on: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
var timestampRunProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Italic(),
new RunFonts { Ascii = fontFamily }
);
@@ -248,8 +249,8 @@ private void AddTimestamp(Body body, string fontFamily, int fontSize)
body.AppendChild(timestampParagraph);
}
- private void AddTableContent(Body body, CsvData input, string fontFamily, int fontSize,
- bool addHeaderRow, bool includeRowNumbers, bool alternateRowColors, bool addBorders,
+ private static void AddTableContent(Body body, CsvData input, string fontFamily, int fontSize,
+ bool addHeaderRow, bool includeRowNumbers, bool alternateRowColors, bool addBorders,
bool autoFitTable, string headerStyle, double tableWidth, bool wrapText)
{
var table = new Table();
@@ -269,7 +270,11 @@ private void AddTableContent(Body body, CsvData input, string fontFamily, int fo
tableProperties.AppendChild(tableBorders);
}
- var tableWidth100 = new TableWidth { Width = (tableWidth * 50).ToString(), Type = TableWidthUnitValues.Pct };
+ var tableWidth100 = new TableWidth
+ {
+ Width = (tableWidth * 50).ToString(System.Globalization.CultureInfo.InvariantCulture),
+ Type = TableWidthUnitValues.Pct
+ };
tableProperties.AppendChild(tableWidth100);
if (autoFitTable)
@@ -289,7 +294,7 @@ private void AddTableContent(Body body, CsvData input, string fontFamily, int fo
if (addHeaderRow)
{
var headerRow = new TableRow();
-
+
foreach (var header in headers)
{
var headerCell = new TableCell();
@@ -329,7 +334,8 @@ private void AddTableContent(Body body, CsvData input, string fontFamily, int fo
if (includeRowNumbers)
{
- var numberCell = CreateDataCell((i + 1).ToString(), fontFamily, fontSize,
+ var numberCell = CreateDataCell((i + 1).ToString(System.Globalization.CultureInfo.InvariantCulture),
+ fontFamily, fontSize,
alternateRowColors && i % 2 == 1, wrapText);
tableRow.AppendChild(numberCell);
}
@@ -337,7 +343,7 @@ private void AddTableContent(Body body, CsvData input, string fontFamily, int fo
for (int j = 0; j < input.Headers.Length; j++)
{
var cellData = j < row.Length ? (row[j] ?? string.Empty) : string.Empty;
- var cell = CreateDataCell(cellData, fontFamily, fontSize,
+ var cell = CreateDataCell(cellData, fontFamily, fontSize,
alternateRowColors && i % 2 == 1, wrapText);
tableRow.AppendChild(cell);
}
@@ -346,16 +352,35 @@ private void AddTableContent(Body body, CsvData input, string fontFamily, int fo
}
body.AppendChild(table);
+
+ if (input.Rows.Count == 0)
+ {
+ var noDataParagraph = new Paragraph();
+ var noDataRun = new Run();
+ var noDataRunProperties = new RunProperties(
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
+ new Italic(),
+ new RunFonts { Ascii = fontFamily }
+ );
+ noDataRun.AppendChild(noDataRunProperties);
+ noDataRun.AppendChild(new Text("No data available."));
+ noDataParagraph.AppendChild(new ParagraphProperties(
+ new Justification { Val = JustificationValues.Center },
+ new SpacingBetweenLines { Before = "120" }
+ ));
+ noDataParagraph.AppendChild(noDataRun);
+ body.AppendChild(noDataParagraph);
+ }
}
- private RunProperties CreateHeaderRunProperties(string fontFamily, int fontSize, string headerStyle)
+ private static RunProperties CreateHeaderRunProperties(string fontFamily, int fontSize, string headerStyle)
{
var runProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new RunFonts { Ascii = fontFamily }
);
- switch (headerStyle.ToLower())
+ switch (headerStyle.ToLowerInvariant())
{
case "strong":
runProperties.AppendChild(new Bold());
@@ -365,14 +390,15 @@ private RunProperties CreateHeaderRunProperties(string fontFamily, int fontSize,
break;
case "heading":
runProperties.AppendChild(new Bold());
- runProperties.AppendChild(new FontSize { Val = ((fontSize + 2) * 2).ToString() });
+ runProperties.AppendChild(new FontSize
+ { Val = ((fontSize + 2) * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) });
break;
}
return runProperties;
}
- private TableCell CreateDataCell(string content, string fontFamily, int fontSize,
+ private static TableCell CreateDataCell(string content, string fontFamily, int fontSize,
bool useAlternateColor, bool wrapText)
{
var cell = new TableCell();
@@ -381,7 +407,7 @@ private TableCell CreateDataCell(string content, string fontFamily, int fontSize
var text = new Text(content);
var runProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new RunFonts { Ascii = fontFamily }
);
@@ -390,7 +416,7 @@ private TableCell CreateDataCell(string content, string fontFamily, int fontSize
paragraph.AppendChild(run);
var cellProperties = new TableCellProperties();
-
+
if (useAlternateColor)
{
var shading = new Shading
@@ -414,7 +440,7 @@ private TableCell CreateDataCell(string content, string fontFamily, int fontSize
return cell;
}
- private void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
+ private static void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
int fontSize, bool includeRowNumbers)
{
for (int i = 0; i < input.Rows.Count; i++)
@@ -426,7 +452,7 @@ private void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
var titleText = new Text($"Record {i + 1}");
var titleRunProperties = new RunProperties(
- new FontSize { Val = ((fontSize + 2) * 2).ToString() },
+ new FontSize { Val = ((fontSize + 2) * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Bold(),
new RunFonts { Ascii = fontFamily }
);
@@ -451,7 +477,7 @@ private void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
var labelText = new Text($"{input.Headers[j]}: ");
var labelRunProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Bold(),
new RunFonts { Ascii = fontFamily }
);
@@ -464,7 +490,7 @@ private void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
var valueText = new Text(row[j] ?? string.Empty);
var valueRunProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new RunFonts { Ascii = fontFamily }
);
@@ -482,7 +508,7 @@ private void AddHierarchicalContent(Body body, CsvData input, string fontFamily,
}
}
- private void AddParagraphContent(Body body, CsvData input, string fontFamily,
+ private static void AddParagraphContent(Body body, CsvData input, string fontFamily,
int fontSize, bool addHeaderRow)
{
if (addHeaderRow)
@@ -492,7 +518,7 @@ private void AddParagraphContent(Body body, CsvData input, string fontFamily,
var headerText = new Text(string.Join(" | ", input.Headers));
var headerRunProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Bold(),
new RunFonts { Ascii = fontFamily }
);
@@ -511,7 +537,7 @@ private void AddParagraphContent(Body body, CsvData input, string fontFamily,
var rowText = new Text(string.Join(" | ", row));
var rowRunProperties = new RunProperties(
- new FontSize { Val = (fontSize * 2).ToString() },
+ new FontSize { Val = (fontSize * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new RunFonts { Ascii = fontFamily }
);
@@ -530,7 +556,7 @@ private void AddStatistics(Body body, CsvData input, string fontFamily, int font
var statsText = new Text($"\nStatistics:\nTotal rows: {input.Rows.Count}\nTotal columns: {input.Headers.Length}\nGenerated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
var statsRunProperties = new RunProperties(
- new FontSize { Val = ((fontSize - 1) * 2).ToString() },
+ new FontSize { Val = ((fontSize - 1) * 2).ToString(System.Globalization.CultureInfo.InvariantCulture) },
new Italic(),
new RunFonts { Ascii = fontFamily }
);
@@ -548,4 +574,4 @@ private void AddStatistics(Body body, CsvData input, string fontFamily, int font
body.AppendChild(statsParagraph);
}
*/
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/CsvToXmlConverter.cs b/src/FileConversionLibrary/Converters/CsvToXmlConverter.cs
similarity index 89%
rename from FileConversionLibrary/Converters/CsvToXmlConverter.cs
rename to src/FileConversionLibrary/Converters/CsvToXmlConverter.cs
index bb169e1..b9619b8 100644
--- a/FileConversionLibrary/Converters/CsvToXmlConverter.cs
+++ b/src/FileConversionLibrary/Converters/CsvToXmlConverter.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using System.Text;
using System.Xml.Linq;
using FileConversionLibrary.Interfaces;
using FileConversionLibrary.Models;
@@ -186,6 +186,7 @@ public string Convert(CsvData input, object? options = null)
rootElement.Add(new XAttribute(XNamespace.Xmlns + ns.Key, ns.Value));
}
}
+
/*
if (includeTimestamp)
{
@@ -217,7 +218,7 @@ public string Convert(CsvData input, object? options = null)
return GetXmlString(doc, preserveWhitespace);
}
- private void ProcessGroupedData(XElement rootElement, CsvData input, string groupByColumnName,
+ private static void ProcessGroupedData(XElement rootElement, CsvData input, string groupByColumnName,
XmlOutputFormat outputFormat, bool useCData, string rowElementName, bool includeRowNumbers,
bool validateXmlNames, XmlNamingConvention namingConvention, bool convertDataTypes)
{
@@ -234,12 +235,13 @@ private void ProcessGroupedData(XElement rootElement, CsvData input, string grou
if (groupColumnIndex >= row.Length) continue;
var groupValue = row[groupColumnIndex] ?? "null";
- if (!groups.ContainsKey(groupValue))
+ if (!groups.TryGetValue(groupValue, out var groupRows))
{
- groups[groupValue] = new List();
+ groupRows = new List();
+ groups[groupValue] = groupRows;
}
- groups[groupValue].Add(row);
+ groupRows.Add(row);
}
foreach (var group in groups)
@@ -256,7 +258,7 @@ private void ProcessGroupedData(XElement rootElement, CsvData input, string grou
}
}
- private void ProcessRowsInGroup(XElement groupElement, CsvData input, List rows,
+ private static void ProcessRowsInGroup(XElement groupElement, CsvData input, List rows,
XmlOutputFormat outputFormat, bool useCData, string rowElementName, bool includeRowNumbers,
bool validateXmlNames, XmlNamingConvention namingConvention, bool convertDataTypes, int skipColumnIndex)
{
@@ -279,7 +281,7 @@ private void ProcessRowsInGroup(XElement groupElement, CsvData input, List GetSortedRows(CsvData input, bool sortRows, string? sortByColumn, bool ascending)
+ private static List GetSortedRows(CsvData input, bool sortRows, string? sortByColumn, bool ascending)
{
if (!sortRows || string.IsNullOrEmpty(sortByColumn))
{
@@ -372,7 +374,8 @@ private List GetSortedRows(CsvData input, bool sortRows, string? sortB
.ToList();
}
- private void ProcessRowData(XElement rowElement, string[] headers, string[] row, XmlOutputFormat outputFormat,
+ private static void ProcessRowData(XElement rowElement, string[] headers, string[] row,
+ XmlOutputFormat outputFormat,
bool useCData, HashSet attributeHeaders, bool validateXmlNames, XmlNamingConvention namingConvention,
bool convertDataTypes, int skipColumnIndex = -1)
{
@@ -438,13 +441,14 @@ private void ProcessRowData(XElement rowElement, string[] headers, string[] row,
}
}
- private XElement CreateRowElement(string rowElementName, bool validateXmlNames,
+ private static XElement CreateRowElement(string rowElementName, bool validateXmlNames,
XmlNamingConvention namingConvention)
{
return new XElement(SanitizeXmlElementName(rowElementName, validateXmlNames, namingConvention));
}
- private HashSet DetermineAttributeFields(CsvData input, XmlOutputFormat format, int skipColumnIndex = -1)
+ private static HashSet DetermineAttributeFields(CsvData input, XmlOutputFormat format,
+ int skipColumnIndex = -1)
{
var result = new HashSet();
@@ -493,7 +497,7 @@ private HashSet DetermineAttributeFields(CsvData input, XmlOutputFormat for
return result;
}
- private object ProcessFieldValue(string value, bool convertDataTypes)
+ private static object ProcessFieldValue(string value, bool convertDataTypes)
{
if (!convertDataTypes || string.IsNullOrEmpty(value))
return value;
@@ -516,7 +520,7 @@ private object ProcessFieldValue(string value, bool convertDataTypes)
return value;
}
- private string GetDataType(object value)
+ private static string GetDataType(object value)
{
return value switch
{
@@ -529,14 +533,14 @@ private string GetDataType(object value)
};
}
- private bool NeedsCData(string value)
+ private static bool NeedsCData(string value)
{
return value.Contains('<') || value.Contains('>') ||
value.Contains('&') || value.Contains('\n') ||
value.Contains("]]>");
}
- private string SanitizeXmlElementName(string name, bool validate, XmlNamingConvention convention)
+ private static string SanitizeXmlElementName(string name, bool validate, XmlNamingConvention convention)
{
if (string.IsNullOrEmpty(name))
return "Field";
@@ -563,7 +567,7 @@ private string SanitizeXmlElementName(string name, bool validate, XmlNamingConve
return sanitized.ToString();
}
- private string ApplyNamingConvention(string name, XmlNamingConvention convention)
+ private static string ApplyNamingConvention(string name, XmlNamingConvention convention)
{
return convention switch
{
@@ -575,43 +579,43 @@ private string ApplyNamingConvention(string name, XmlNamingConvention convention
};
}
- private string ToCamelCase(string input)
+ private static string ToCamelCase(string input)
{
if (string.IsNullOrEmpty(input)) return input;
var words = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
- var result = words[0].ToLower();
+ var result = words[0].ToLowerInvariant();
for (int i = 1; i < words.Length; i++)
{
- result += char.ToUpper(words[i][0]) + words[i].Substring(1).ToLower();
+ result += char.ToUpperInvariant(words[i][0]) + words[i].Substring(1).ToLowerInvariant();
}
return result;
}
- private string ToPascalCase(string input)
+ private static string ToPascalCase(string input)
{
if (string.IsNullOrEmpty(input)) return input;
var words = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var result = string.Empty;
foreach (var word in words)
{
- result += char.ToUpper(word[0]) + word.Substring(1).ToLower();
+ result += char.ToUpperInvariant(word[0]) + word.Substring(1).ToLowerInvariant();
}
return result;
}
- private string ToKebabCase(string input)
+ private static string ToKebabCase(string input)
{
- return input.ToLower().Replace(' ', '-');
+ return input.ToLowerInvariant().Replace(' ', '-');
}
- private string ToSnakeCase(string input)
+ private static string ToSnakeCase(string input)
{
- return input.ToLower().Replace(' ', '_');
+ return input.ToLowerInvariant().Replace(' ', '_');
}
- private string EscapeXmlAttributeValue(string value)
+ private static string EscapeXmlAttributeValue(string value)
{
if (string.IsNullOrEmpty(value))
return value;
@@ -623,18 +627,18 @@ private string EscapeXmlAttributeValue(string value)
.Replace("\"", """)
.Replace("'", "'");
}
-
- private string GetXmlString(XDocument doc, bool preserveWhitespace)
+
+ private static string GetXmlString(XDocument doc, bool preserveWhitespace)
{
var sb = new StringBuilder();
-
+
var version = doc.Declaration?.Version ?? "1.0";
var encoding = doc.Declaration?.Encoding ?? "UTF-8";
- sb.AppendLine($"");
-
+ sb.Append(FormattableString.Invariant($"")).AppendLine();
+
var saveOptions = preserveWhitespace ? SaveOptions.None : SaveOptions.DisableFormatting;
sb.Append(doc.ToString(saveOptions));
-
+
return sb.ToString();
}
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/CsvToYamlConverter.cs b/src/FileConversionLibrary/Converters/CsvToYamlConverter.cs
similarity index 91%
rename from FileConversionLibrary/Converters/CsvToYamlConverter.cs
rename to src/FileConversionLibrary/Converters/CsvToYamlConverter.cs
index 3b5e4aa..2b2a654 100644
--- a/FileConversionLibrary/Converters/CsvToYamlConverter.cs
+++ b/src/FileConversionLibrary/Converters/CsvToYamlConverter.cs
@@ -155,7 +155,7 @@ public string Convert(CsvData input, object? options = null)
}
var sb = new StringBuilder();
-
+
if (includeMetadata)
{
//AddMetadata(sb, input, addTimestamp, customMetadata, indentSize);
@@ -188,6 +188,7 @@ public string Convert(CsvData input, object? options = null)
return sb.ToString();
}
+
/*
private void AddMetadata(StringBuilder sb, CsvData input, bool addTimestamp,
Dictionary? customMetadata, int indentSize)
@@ -224,7 +225,7 @@ private void AddMetadata(StringBuilder sb, CsvData input, bool addTimestamp,
sb.AppendLine();
}
*/
- private void ConvertToArray(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
+ private static void ConvertToArray(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
bool convertDataTypes, int indentSize, bool useFlowStyle, bool sortKeys, bool includeNullValues,
bool compactArrays, string? dateFormat, bool escapeSpecialChars, bool preserveQuotes)
{
@@ -299,7 +300,7 @@ private void ConvertToArray(StringBuilder sb, CsvData input, YamlNamingConventio
}
}
- private void ConvertToDictionary(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
+ private static void ConvertToDictionary(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
bool convertDataTypes, int indentSize, bool useFlowStyle, bool sortKeys, bool includeNullValues,
string? dateFormat, bool escapeSpecialChars, bool preserveQuotes)
{
@@ -356,7 +357,7 @@ private void ConvertToDictionary(StringBuilder sb, CsvData input, YamlNamingConv
}
}
- private void ConvertToHierarchical(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
+ private static void ConvertToHierarchical(StringBuilder sb, CsvData input, YamlNamingConvention namingConvention,
bool convertDataTypes, int indentSize, bool sortKeys, bool includeNullValues, string? customRootKey,
string? dateFormat, bool escapeSpecialChars, bool preserveQuotes)
{
@@ -415,7 +416,7 @@ private void ConvertToHierarchical(StringBuilder sb, CsvData input, YamlNamingCo
}
}
- private void ConvertToGrouped(StringBuilder sb, CsvData input, string? groupByColumn,
+ private static void ConvertToGrouped(StringBuilder sb, CsvData input, string? groupByColumn,
YamlNamingConvention namingConvention, bool convertDataTypes, int indentSize, bool sortKeys,
bool includeNullValues, string? dateFormat, bool escapeSpecialChars, bool preserveQuotes)
{
@@ -438,12 +439,13 @@ private void ConvertToGrouped(StringBuilder sb, CsvData input, string? groupByCo
if (groupColumnIndex >= row.Length) continue;
var groupValue = row[groupColumnIndex] ?? "null";
- if (!groups.ContainsKey(groupValue))
+ if (!groups.TryGetValue(groupValue, out var groupList))
{
- groups[groupValue] = new List();
+ groupList = new List();
+ groups[groupValue] = groupList;
}
- groups[groupValue].Add(row);
+ groupList.Add(row);
}
sb.AppendLine("groups:");
@@ -511,7 +513,7 @@ private void ConvertToGrouped(StringBuilder sb, CsvData input, string? groupByCo
}
}
- private HashSet DetectMultilineColumns(CsvData input)
+ private static HashSet DetectMultilineColumns(CsvData input)
{
var result = new HashSet();
@@ -530,7 +532,7 @@ private HashSet DetectMultilineColumns(CsvData input)
return result;
}
- private string DetectColumnDataType(CsvData input, string headerName)
+ private static string DetectColumnDataType(CsvData input, string headerName)
{
var headerIndex = Array.IndexOf(input.Headers, headerName);
if (headerIndex == -1) return "string";
@@ -541,7 +543,7 @@ private string DetectColumnDataType(CsvData input, string headerName)
.Take(10)
.ToList();
- if (!sampleValues.Any()) return "string";
+ if (sampleValues.Count == 0) return "string";
if (sampleValues.All(v => int.TryParse(v, out _))) return "integer";
if (sampleValues.All(v => double.TryParse(v, out _))) return "number";
@@ -551,7 +553,8 @@ private string DetectColumnDataType(CsvData input, string headerName)
return "string";
}
- private object? ProcessValue(string? value, bool convertDataTypes, string? dateFormat, bool escapeSpecialChars)
+ private static object? ProcessValue(string? value, bool convertDataTypes, string? dateFormat,
+ bool escapeSpecialChars)
{
if (string.IsNullOrEmpty(value))
return null;
@@ -593,7 +596,7 @@ private string DetectColumnDataType(CsvData input, string headerName)
return escapeSpecialChars ? EscapeYamlString(value) : value;
}
- private YamlStyle DetermineYamlStyle(string? value, bool isMultiline)
+ private static YamlStyle DetermineYamlStyle(string? value, bool isMultiline)
{
if (string.IsNullOrEmpty(value))
return YamlStyle.Default;
@@ -610,7 +613,7 @@ private YamlStyle DetermineYamlStyle(string? value, bool isMultiline)
return YamlStyle.Default;
}
- private string FormatYamlKey(string key, YamlNamingConvention convention)
+ private static string FormatYamlKey(string key, YamlNamingConvention convention)
{
if (string.IsNullOrEmpty(key))
return "field";
@@ -620,12 +623,12 @@ private string FormatYamlKey(string key, YamlNamingConvention convention)
YamlNamingConvention.CamelCase => ToCamelCase(key),
YamlNamingConvention.SnakeCase => ToSnakeCase(key),
YamlNamingConvention.KebabCase => ToKebabCase(key),
- YamlNamingConvention.LowerCase => key.ToLower(),
+ YamlNamingConvention.LowerCase => key.ToLowerInvariant(),
_ => key
};
}
- private string FormatYamlValue(object? value, bool isMultiline, string? dateFormat,
+ private static string FormatYamlValue(object? value, bool isMultiline, string? dateFormat,
bool escapeSpecialChars, bool preserveQuotes)
{
if (value == null)
@@ -639,7 +642,7 @@ private string FormatYamlValue(object? value, bool isMultiline, string? dateForm
}
if (value is bool boolValue)
- return boolValue.ToString().ToLower();
+ return boolValue.ToString().ToLowerInvariant();
if (value is int || value is double || value is decimal)
return value.ToString() ?? "0";
@@ -658,46 +661,46 @@ private string FormatYamlValue(object? value, bool isMultiline, string? dateForm
return stringValue;
}
- private string ToCamelCase(string input)
+ private static string ToCamelCase(string input)
{
if (string.IsNullOrEmpty(input)) return input;
- var words = input.Split(new[] { ' ', '_', '-' }, StringSplitOptions.RemoveEmptyEntries);
+ var words = input.Split([' ', '_', '-'], StringSplitOptions.RemoveEmptyEntries);
if (words.Length == 0) return input;
- var result = words[0].ToLower();
+ var result = words[0].ToLowerInvariant();
for (int i = 1; i < words.Length; i++)
{
if (words[i].Length > 0)
{
- result += char.ToUpper(words[i][0]) + words[i].Substring(1).ToLower();
+ result += char.ToUpperInvariant(words[i][0]) + words[i].Substring(1).ToLowerInvariant();
}
}
return result;
}
- private string ToSnakeCase(string input)
+ private static string ToSnakeCase(string input)
{
- return input.ToLower().Replace(" ", "_").Replace("-", "_");
+ return input.ToLowerInvariant().Replace(" ", "_").Replace("-", "_");
}
- private string ToKebabCase(string input)
+ private static string ToKebabCase(string input)
{
- return input.ToLower().Replace(" ", "-").Replace("_", "-");
+ return input.ToLowerInvariant().Replace(" ", "-").Replace("_", "-");
}
- private bool NeedsQuoting(string value)
+ private static bool NeedsQuoting(string value)
{
if (string.IsNullOrEmpty(value))
return false;
- return value.Contains(":") ||
- value.Contains("#") ||
- value.Contains("'") ||
- value.Contains("\"") ||
- value.StartsWith("-") ||
- value.StartsWith("[") ||
- value.StartsWith("{") ||
+ return value.Contains(':') ||
+ value.Contains('#') ||
+ value.Contains('\'') ||
+ value.Contains('"') ||
+ value.StartsWith('-') ||
+ value.StartsWith('[') ||
+ value.StartsWith('{') ||
value == "null" ||
value == "true" ||
value == "false" ||
@@ -709,7 +712,7 @@ private bool NeedsQuoting(string value)
char.IsWhiteSpace(value[^1]);
}
- private string EscapeYamlString(string value)
+ private static string EscapeYamlString(string value)
{
if (string.IsNullOrEmpty(value))
return value;
@@ -722,8 +725,8 @@ private string EscapeYamlString(string value)
.Replace("\n", "\\n");
}
- private string GetIndent(int level)
+ private static string GetIndent(int level)
{
return new string(' ', level);
}
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/InMemoryConverter.cs b/src/FileConversionLibrary/Converters/InMemoryConverter.cs
similarity index 95%
rename from FileConversionLibrary/Converters/InMemoryConverter.cs
rename to src/FileConversionLibrary/Converters/InMemoryConverter.cs
index 7bc41e5..026e820 100644
--- a/FileConversionLibrary/Converters/InMemoryConverter.cs
+++ b/src/FileConversionLibrary/Converters/InMemoryConverter.cs
@@ -1,4 +1,4 @@
-using FileConversionLibrary.Interfaces;
+using FileConversionLibrary.Interfaces;
using FileConversionLibrary.Models;
using FileConversionLibrary.Factories;
using FileConversionLibrary.Enums;
@@ -167,7 +167,7 @@ public string ConvertXmlToYaml(XmlData data, YamlConversionOptions? options = nu
}
}
- private Dictionary? ConvertToOptionsDict(ConversionOptions? options)
+ private static Dictionary? ConvertToOptionsDict(ConversionOptions? options)
{
if (options == null) return null;
@@ -177,7 +177,7 @@ public string ConvertXmlToYaml(XmlData data, YamlConversionOptions? options = nu
foreach (var prop in properties)
{
if (prop.Name == nameof(ConversionOptions.CustomProperties)) continue;
-
+
var value = prop.GetValue(options);
if (value != null)
{
@@ -198,6 +198,6 @@ private static string ToCamelCase(string input)
if (string.IsNullOrEmpty(input) || char.IsLower(input[0]))
return input;
- return char.ToLower(input[0]) + input.Substring(1);
+ return char.ToLower(input[0], System.Globalization.CultureInfo.InvariantCulture) + input.Substring(1);
}
-}
\ No newline at end of file
+}
diff --git a/FileConversionLibrary/Converters/StreamConverter.cs b/src/FileConversionLibrary/Converters/StreamConverter.cs
similarity index 96%
rename from FileConversionLibrary/Converters/StreamConverter.cs
rename to src/FileConversionLibrary/Converters/StreamConverter.cs
index 833da01..d14040b 100644
--- a/FileConversionLibrary/Converters/StreamConverter.cs
+++ b/src/FileConversionLibrary/Converters/StreamConverter.cs
@@ -1,4 +1,4 @@
-using FileConversionLibrary.Interfaces;
+using FileConversionLibrary.Interfaces;
using FileConversionLibrary.Models;
using FileConversionLibrary.Readers;
using System.Text;
@@ -37,7 +37,7 @@ public async Task ConvertToBytesAsync(Stream input, ConversionOptions op
{
var data = await ReadDataFromStream(input, options.SourceFormat);
- return options.TargetFormat.ToLower() switch
+ return options.TargetFormat.ToLowerInvariant() switch
{
"pdf" => ConvertToPdfBytes(data, options),
"word" or "docx" => ConvertToWordBytes(data, options),
@@ -57,7 +57,7 @@ public async Task ConvertToStringAsync(Stream input, ConversionOptions o
{
var data = await ReadDataFromStream(input, options.SourceFormat);
- return options.TargetFormat.ToLower() switch
+ return options.TargetFormat.ToLowerInvariant() switch
{
"json" => ConvertToJsonString(data, options),
"xml" => ConvertToXmlString(data, options),
@@ -78,7 +78,7 @@ private async Task