diff --git a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java
index a7336c244d458f062819dc4536b699744e665950..a62c58af97e8b2fb96cc9c8f0e31128a20a5d35c 100644
--- a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java
+++ b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java
@@ -23,43 +23,72 @@
  */
 package de.ozgcloud.eingang.formsolutions;
 
+import static java.util.Collections.*;
+
 import java.io.File;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+import jakarta.annotation.Nullable;
+
 import org.springframework.stereotype.Component;
 
 import de.ozgcloud.eingang.common.formdata.IncomingFile;
 import de.ozgcloud.eingang.common.formdata.IncomingFileGroup;
+import de.ozgcloud.eingang.semantik.common.ZipAttachmentReader;
+import lombok.extern.log4j.Log4j2;
 
+@Log4j2
 @Component
 class FormSolutionsAttachmentsMapper {
 
 	public static final String ZIP = "zip";
 	public static final String FILE_NAME_ZIP_ATTACHMENT = "attachments.zip";
 	public static final String ZIP_CONTENT_TYPE = "application/zip";
-	public static final String FILE_GROUP_ZIP_NAME = "gezippte Anhänge";
 
-	public List<IncomingFileGroup> mapAttachments(File zipFile) {
+	public List<IncomingFileGroup> mapAttachments(@Nullable File zipFile) {
 		if (Objects.nonNull(zipFile) && zipFile.length() > 0) {
-			return Collections.singletonList(buildFileGroup(buildZipFile(zipFile)));
+			return extractAttachmentGroups(zipFile);
 		}
-		return Collections.emptyList();
+		return emptyList();
 	}
 
-	private IncomingFileGroup buildFileGroup(IncomingFile zipFile) {
-		return IncomingFileGroup.builder()
-				.name(FILE_GROUP_ZIP_NAME)
-				.files(List.of(zipFile))
-				.build();
+	List<IncomingFileGroup> extractAttachmentGroups(File zipFile) {
+		var attachmentIncomingFiles = extractAttachments(zipFile);
+		return attachmentIncomingFiles.isEmpty()
+				? emptyList()
+				: List.of(IncomingFileGroup.builder()
+						.name("Anhänge")
+						.files(attachmentIncomingFiles)
+						.build());
+	}
+
+	List<IncomingFile> extractAttachments(File zipFile) {
+		try {
+			return readFromZip(zipFile);
+		} catch (RuntimeException e) {
+			LOG.error("Cannot read source ZIP. Attach it as is.", e);
+			return List.of(buildZipFile(zipFile));
+		}
 	}
 
-	private IncomingFile buildZipFile(File zipFile) {
+	IncomingFile buildZipFile(File zipFile) {
 		return IncomingFile.builder()
 				.file(zipFile)
 				.contentType(ZIP_CONTENT_TYPE)
 				.name(FILE_NAME_ZIP_ATTACHMENT)
 				.build();
 	}
+
+	List<IncomingFile> readFromZip(File zipFile) {
+		var reader = buildReader(zipFile);
+		var readContent = reader.readContent();
+		reader.deleteSourceFile();
+		return readContent;
+	}
+
+	ZipAttachmentReader buildReader(File zipFile) {
+		return ZipAttachmentReader.from(zipFile, null);
+	}
+
 }
\ No newline at end of file
diff --git a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java
index c62262d90f231c1f1d9b91b23094fcec72ff889b..af212bdc945a13cc038ad756e4fe4bc4d53f274a 100644
--- a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java
+++ b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java
@@ -26,6 +26,7 @@ package de.ozgcloud.eingang.formsolutions;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -40,6 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.eingang.common.formdata.FormData;
 import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.semantik.enginebased.FilesMapperHelper;
 import lombok.RequiredArgsConstructor;
 
 @Component
@@ -67,22 +69,25 @@ class FormSolutionsRequestMapper {
 	}
 
 	FormData buildFormData(File jsonFile, FormSolutionsEingang eingang) {
-		var builder = FormData.builder()
-				.attachments(attachmentMapper.mapAttachments(eingang.getZip()))
-				.representation(buildJsonFile(jsonFile));
-		var numberOfRepresentations = 1;
-
-		if (Objects.nonNull(eingang.getPdf())) {
-			builder.representation(buildPdfFile(eingang.getPdf()));
-			numberOfRepresentations++;
-		}
+		var attachments = attachmentMapper.mapAttachments(eingang.getZip());
+		var representations = getRepresentations(jsonFile, eingang);
 
-		return builder
+		return FormData.builder()
+				.attachments(attachments)
+				.representations(representations)
 				.control(buildFormDataControl())
-				.numberOfRepresentations(numberOfRepresentations)
+				.numberOfRepresentations(representations.size())
+				.numberOfAttachments(FilesMapperHelper.countAttachedFiles(attachments))
 				.build();
 	}
 
+	List<IncomingFile> getRepresentations(File jsonFile, FormSolutionsEingang eingang) {
+		var jsonRepresentation = buildJsonFile(jsonFile);
+		return Objects.nonNull(eingang.getPdf())
+				? List.of(jsonRepresentation, buildPdfFile(eingang.getPdf()))
+				: List.of(jsonRepresentation);
+	}
+
 	FormData.FormDataControl buildFormDataControl() {
 		return FormData.FormDataControl.builder()
 				.representations(Optional.of(buildControlRepresentations()))
diff --git a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java
index 01ea26c58bd8f407da8a5ea7e9fd9cbb72aa6dcf..d220f095cb745339909d1d2851930e99832a0080 100644
--- a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java
+++ b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java
@@ -24,74 +24,273 @@
 package de.ozgcloud.eingang.formsolutions;
 
 import static de.ozgcloud.eingang.formsolutions.FormSolutionsAttachmentsMapper.*;
-import static de.ozgcloud.eingang.formsolutions.FormSolutionsFilesTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 
 import java.io.File;
 import java.util.List;
 
-import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
 
-import de.ozgcloud.common.binaryfile.TempFileUtils;
-import de.ozgcloud.common.test.TestUtils;
 import de.ozgcloud.eingang.common.formdata.IncomingFile;
 import de.ozgcloud.eingang.common.formdata.IncomingFileGroup;
-import lombok.SneakyThrows;
+import de.ozgcloud.eingang.semantik.common.ZipAttachmentReader;
 
 class FormSolutionsAttachmentsMapperTest {
 
+	@Spy
 	@InjectMocks
 	private FormSolutionsAttachmentsMapper mapper;
 
+	@Mock
 	private File zipFile;
 
-	@BeforeEach
-	void writeZipFile() {
-		zipFile = TempFileUtils.writeTmpFile(ZIP_DECODED);
-	}
+	@DisplayName("map attachments")
+	@Nested
+	class TestMapAttachments {
+
+		@Mock
+		private IncomingFileGroup incomingFileGroup;
+
+		@DisplayName("with valid file")
+		@Nested
+		class TestWithValidFile {
+			@BeforeEach
+			void mock() {
+				when(zipFile.length()).thenReturn(1L);
+				doReturn(List.of(incomingFileGroup)).when(mapper).extractAttachmentGroups(any());
+			}
+
+			@DisplayName("should call extractAttachmentGroups")
+			@Test
+			void shouldCallExtractAttachmentGroups() {
+				mapper.mapAttachments(zipFile);
+
+				verify(mapper).extractAttachmentGroups(zipFile);
+			}
+
+			@DisplayName("should return")
+			@Test
+			void shouldReturn() {
+				var result = mapper.mapAttachments(zipFile);
+
+				assertThat(result).containsExactly(incomingFileGroup);
+			}
+		}
 
-	@AfterEach
-	void delZipFile() {
-		zipFile.delete();
+		@DisplayName("should return empty if file null")
+		@Test
+		void shouldReturnEmptyIfFileNull() {
+			var result = mapper.mapAttachments(null);
+
+			assertThat(result).isEmpty();
+		}
+
+		@DisplayName("should return empty if file empty")
+		@Test
+		void shouldReturnEmptyIfFileEmpty() {
+			when(zipFile.length()).thenReturn(0L);
+
+			var result = mapper.mapAttachments(zipFile);
+
+			assertThat(result).isEmpty();
+		}
 	}
 
+	@DisplayName("extract attachment groups")
 	@Nested
-	class TestAttachmentsMapping {
+	class TestExtractAttachmentGroups {
+
+		@Mock
+		private IncomingFile incomingFile;
+
+		@DisplayName("with files")
+		@Nested
+		class TestWithFiles {
+			@BeforeEach
+			void mock() {
+				doReturn(List.of(incomingFile)).when(mapper).extractAttachments(any());
+			}
+
+			@DisplayName("should call extractAttachments")
+			@Test
+			void shouldCallExtractAttachments() {
+				mapper.extractAttachmentGroups(zipFile);
+
+				verify(mapper).extractAttachments(zipFile);
+			}
+
+			@DisplayName("should return")
+			@Test
+			void shouldReturn() {
+				var result = mapper.extractAttachmentGroups(zipFile);
+
+				assertThat(result)
+						.flatExtracting(IncomingFileGroup::getFiles)
+						.containsExactly(incomingFile);
+			}
+
+			@DisplayName("should return name")
+			@Test
+			void shouldReturnName() {
+				var result = mapper.extractAttachmentGroups(zipFile);
+
+				assertThat(result)
+						.flatExtracting(IncomingFileGroup::getName)
+						.containsExactly("Anhänge");
+			}
+		}
+
+		@DisplayName("should return empty with no files")
 		@Test
-		@SneakyThrows
-		void shouldParseZip() {
-			var map = mapper.mapAttachments(zipFile);
+		void shouldReturnEmptyWithNoFiles() {
+			doReturn(List.of()).when(mapper).extractAttachments(any());
+
+			var result = mapper.extractAttachmentGroups(zipFile);
+
+			assertThat(result).isEmpty();
+		}
+	}
+
+	@DisplayName("extract attachments")
+	@Nested
+	class TestExtractAttachments {
+		@Mock
+		private IncomingFile incomingFile;
 
-			assertThat(TestUtils.contentStreamToByteArray(getAttachment(map).getContentStream())).isEqualTo(ZIP_DECODED);
+		@DisplayName("without exception")
+		@Nested
+		class TestWithoutException {
+			@BeforeEach
+			void mock() {
+				doReturn(List.of(incomingFile)).when(mapper).readFromZip(any());
+
+			}
+
+			@DisplayName("should call readFromZip")
+			@Test
+			void shouldCallReadFromZip() {
+				mapper.extractAttachments(zipFile);
+
+				verify(mapper).readFromZip(zipFile);
+			}
+
+			@DisplayName("should return")
+			@Test
+			void shouldReturn() {
+				var result = mapper.extractAttachments(zipFile);
+
+				assertThat(result).containsExactly(incomingFile);
+			}
 		}
 
+		@DisplayName("with exception")
+		@Nested
+		class TestWithException {
+			@BeforeEach
+			void mock() {
+				doThrow(new RuntimeException()).when(mapper).readFromZip(any());
+				doReturn(incomingFile).when(mapper).buildZipFile(any());
+			}
+
+			@DisplayName("should call buildZipFile")
+			@Test
+			void shouldCallBuildZipFile() {
+				mapper.extractAttachments(zipFile);
+
+				verify(mapper).buildZipFile(zipFile);
+			}
+
+			@DisplayName("should return zip")
+			@Test
+			void shouldReturnZip() {
+				var result = mapper.extractAttachments(zipFile);
+
+				assertThat(result).containsExactly(incomingFile);
+			}
+		}
+	}
+
+	@DisplayName("build zip file")
+	@Nested
+	class TestBuildZipFile {
+		@DisplayName("should map file")
 		@Test
-		void shouldSetContentType() {
-			var map = mapper.mapAttachments(zipFile);
+		void shouldMapFile() {
+			var result = mapper.buildZipFile(zipFile);
 
-			assertThat(getAttachment(map).getContentType()).isEqualTo(ZIP_CONTENT_TYPE);
+			assertThat(result.getFile()).isEqualTo(zipFile);
 		}
 
+		@DisplayName("should map content type")
 		@Test
-		void shouldSetFileName() {
-			var map = mapper.mapAttachments(zipFile);
+		void shouldMapContentType() {
+			var result = mapper.buildZipFile(zipFile);
 
-			assertThat(getAttachment(map).getName()).isEqualTo(FILE_NAME_ZIP_ATTACHMENT);
+			assertThat(result.getContentType()).isEqualTo(ZIP_CONTENT_TYPE);
 		}
 
+		@DisplayName("should map name")
 		@Test
-		void shouldSetGroupName() {
-			var map = mapper.mapAttachments(zipFile);
+		void shouldMapName() {
+			var result = mapper.buildZipFile(zipFile);
 
-			assertThat(map.get(0).getName()).isEqualTo(FILE_GROUP_ZIP_NAME);
+			assertThat(result.getName()).isEqualTo(FILE_NAME_ZIP_ATTACHMENT);
 		}
 	}
 
-	private IncomingFile getAttachment(List<IncomingFileGroup> attachments) {
-		return attachments.get(0).getFiles().get(0);
+	@DisplayName("read from zip")
+	@Nested
+	class TestReadFromZip {
+		@Mock
+		private IncomingFile incomingFile;
+
+		@Mock
+		private ZipAttachmentReader reader;
+
+		@BeforeEach
+		void mock() {
+			doReturn(reader).when(mapper).buildReader(any());
+			when(reader.readContent()).thenReturn(List.of(incomingFile));
+		}
+
+		@DisplayName("should call buildReader")
+		@Test
+		void shouldCallBuildReader() {
+			mapper.readFromZip(zipFile);
+
+			verify(mapper).buildReader(zipFile);
+		}
+
+		@DisplayName("should call readContent")
+		@Test
+		void shouldCallReadContent() {
+			mapper.readFromZip(zipFile);
+
+			verify(reader).readContent();
+		}
+
+		@DisplayName("should call deleteSourceFile")
+		@Test
+		void shouldCallDeleteSourceFile() {
+			mapper.readFromZip(zipFile);
+
+			verify(reader).deleteSourceFile();
+		}
+
+		@DisplayName("should return")
+		@Test
+		void shouldReturn() {
+			var result = mapper.readFromZip(zipFile);
+
+			assertThat(result).containsExactly(incomingFile);
+		}
 	}
 }
diff --git a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java
index b5627d9b795710d4e8612f830c41a0cd69bc822a..d8f5a1f7ab39bb8982ca735599c643669832fc1a 100644
--- a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java
+++ b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java
@@ -138,8 +138,7 @@ class FormSolutionsRequestMapperTest {
 		@Mock
 		private File zipFile;
 
-		@Mock
-		private IncomingFileGroup attachmentGroup;
+		private final IncomingFileGroup attachmentGroup = IncomingFileGroupTestFactory.create();
 
 		@Mock
 		private IncomingFile jsonIncomingFile;
@@ -153,9 +152,9 @@ class FormSolutionsRequestMapperTest {
 		@BeforeEach
 		void mock() {
 			when(eingang.getZip()).thenReturn(zipFile);
-
 			when(attachmentMapper.mapAttachments(any())).thenReturn(List.of(attachmentGroup));
-			doReturn(jsonIncomingFile).when(mapper).buildJsonFile(any());
+
+			doReturn(List.of(jsonIncomingFile)).when(mapper).getRepresentations(any(), any());
 			doReturn(formDataControl).when(mapper).buildFormDataControl();
 		}
 
@@ -175,12 +174,12 @@ class FormSolutionsRequestMapperTest {
 			assertThat(result.getAttachments()).containsExactly(attachmentGroup);
 		}
 
-		@DisplayName("should call buildJsonFile")
+		@DisplayName("should call getRepresentations")
 		@Test
-		void shouldCallBuildJsonFile() {
+		void shouldCallGetRepresentations() {
 			buildFormData();
 
-			verify(mapper).buildJsonFile(simpleJsonFile);
+			verify(mapper).getRepresentations(simpleJsonFile, eingang);
 		}
 
 		@DisplayName("should return representations")
@@ -199,10 +198,60 @@ class FormSolutionsRequestMapperTest {
 			assertThat(result.getControl()).isEqualTo(formDataControl);
 		}
 
+		@DisplayName("should return numberOfRepresentations")
+		@Test
+		void shouldReturnNumberOfRepresentations() {
+			var result = buildFormData();
+
+			assertThat(result.getNumberOfRepresentations()).isEqualTo(1);
+		}
+
+		@DisplayName("should return numberOfAttachments")
+		@Test
+		void shouldReturnNumberOfAttachments() {
+			var result = buildFormData();
+
+			assertThat(result.getNumberOfAttachments()).isEqualTo(1);
+		}
+
+		private FormData buildFormData() {
+			return mapper.buildFormData(simpleJsonFile, eingang);
+		}
+	}
+
+	@DisplayName("get representations")
+	@Nested
+	class TestGetRepresentations {
+
+		@Mock
+		private FormSolutionsEingang eingang;
+
+		@Mock
+		private IncomingFile jsonIncomingFile;
+
+		@BeforeEach
+		void mock() {
+			doReturn(jsonIncomingFile).when(mapper).buildJsonFile(any());
+		}
+
+		@DisplayName("should call buildJsonFile")
+		@Test
+		void shouldCallBuildJsonFile() {
+			getRepresentations();
+
+			verify(mapper).buildJsonFile(simpleJsonFile);
+		}
+
 		@DisplayName("with pdf")
 		@Nested
 		class TestWithPdf {
 
+			@Mock
+			private File pdfFile;
+
+			@Mock
+			private IncomingFile pdfIncomingFile;
+
 			@BeforeEach
 			void mock() {
 				when(eingang.getPdf()).thenReturn(pdfFile);
@@ -212,7 +261,7 @@ class FormSolutionsRequestMapperTest {
 			@DisplayName("should call buildPdfFile")
 			@Test
 			void shouldCallBuildPdfFile() {
-				buildFormData();
+				getRepresentations();
 
 				verify(mapper).buildPdfFile(eingang.getPdf());
 			}
@@ -220,14 +269,24 @@ class FormSolutionsRequestMapperTest {
 			@DisplayName("should return representations")
 			@Test
 			void shouldReturnRepresentations() {
-				var result = buildFormData();
+				var result = getRepresentations();
 
-				assertThat(result.getRepresentations()).containsExactly(jsonIncomingFile, pdfIncomingFile);
+				assertThat(result).containsExactly(jsonIncomingFile, pdfIncomingFile);
 			}
 		}
 
-		private FormData buildFormData() {
-			return mapper.buildFormData(simpleJsonFile, eingang);
+		@DisplayName("should return without pdf")
+		@Test
+		void shouldReturnWithoutPdf() {
+			when(eingang.getPdf()).thenReturn(null);
+
+			var result = getRepresentations();
+
+			assertThat(result).containsExactly(jsonIncomingFile);
+		}
+
+		private List<IncomingFile> getRepresentations() {
+			return mapper.getRepresentations(simpleJsonFile, eingang);
 		}
 	}