diff --git a/app/src/main.rs b/app/src/main.rs
index fbcb2e7..561a6bd 100644
--- a/app/src/main.rs
+++ b/app/src/main.rs
@@ -10,7 +10,7 @@ use chgk_ledb_lib::db;
 use chgk_ledb_lib::questions;
 use chgk_ledb_lib::source;
 
-use crate::questions::{Question, QuestionsConverter};
+use crate::questions::{binary::Question, QuestionsConverter};
 use crate::source::ReadSourceQuestionsBatches;
 
 const ZIP_FILENAME: &str = "json.zip";
diff --git a/app_async/src/main.rs b/app_async/src/main.rs
index 88e1256..996ee02 100644
--- a/app_async/src/main.rs
+++ b/app_async/src/main.rs
@@ -16,7 +16,7 @@ use tokio::{fs, io};
 use tokio_stream::wrappers::UnboundedReceiverStream;
 
 use chgk_ledb_lib::async_db;
-use chgk_ledb_lib::questions::Question;
+use chgk_ledb_lib::questions::binary::Question;
 use chgk_ledb_lib::questions::QuestionsConverterAsyncForStream;
 use chgk_ledb_lib::source::ReadSourceQuestionsBatchesAsync;
 
diff --git a/lib/src/questions.rs b/lib/src/questions.rs
index 3f5ee68..9039d7a 100644
--- a/lib/src/questions.rs
+++ b/lib/src/questions.rs
@@ -2,74 +2,264 @@ use serde_derive::{Deserialize, Serialize};
 
 #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
 pub struct BatchInfo {
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub filename: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub description: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub author: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub comment: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub url: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub date: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub processed_by: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub redacted_by: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub copyright: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub theme: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub kind: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub source: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub rating: String,
 }
 
 #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
 pub struct Question {
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "u32_is_zero")]
     pub num: u32,
     pub id: String,
+
     pub description: String,
     pub answer: String,
-    #[serde(default)]
+
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub author: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub comment: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub comment1: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub tour: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub url: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub date: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub processed_by: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub redacted_by: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub copyright: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub theme: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub kind: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub source: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "String::is_empty")]
     pub rating: String,
-    #[serde(default)]
+    #[serde(default, skip_serializing_if = "BatchInfo::is_default")]
     pub batch_info: BatchInfo,
 }
 
+fn u32_is_zero(num: &u32) -> bool {
+    *num == 0
+}
+
+impl BatchInfo {
+    pub fn is_default(&self) -> bool {
+        *self == BatchInfo::default()
+    }
+}
+
+pub mod binary {
+    use serde_derive::{Deserialize, Serialize};
+    #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
+    pub struct BatchInfo {
+        #[serde(default)]
+        pub filename: String,
+        #[serde(default)]
+        pub description: String,
+        #[serde(default)]
+        pub author: String,
+        #[serde(default)]
+        pub comment: String,
+        #[serde(default)]
+        pub url: String,
+        #[serde(default)]
+        pub date: String,
+        #[serde(default)]
+        pub processed_by: String,
+        #[serde(default)]
+        pub redacted_by: String,
+        #[serde(default)]
+        pub copyright: String,
+        #[serde(default)]
+        pub theme: String,
+        #[serde(default)]
+        pub kind: String,
+        #[serde(default)]
+        pub source: String,
+        #[serde(default)]
+        pub rating: String,
+    }
+
+    #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
+    pub struct Question {
+        #[serde(default)]
+        pub num: u32,
+        pub id: String,
+        pub description: String,
+        pub answer: String,
+        #[serde(default)]
+        pub author: String,
+        #[serde(default)]
+        pub comment: String,
+        #[serde(default)]
+        pub comment1: String,
+        #[serde(default)]
+        pub tour: String,
+        #[serde(default)]
+        pub url: String,
+        #[serde(default)]
+        pub date: String,
+        #[serde(default)]
+        pub processed_by: String,
+        #[serde(default)]
+        pub redacted_by: String,
+        #[serde(default)]
+        pub copyright: String,
+        #[serde(default)]
+        pub theme: String,
+        #[serde(default)]
+        pub kind: String,
+        #[serde(default)]
+        pub source: String,
+        #[serde(default)]
+        pub rating: String,
+        #[serde(default)]
+        pub batch_info: BatchInfo,
+    }
+
+    #[cfg(test)]
+    mod test {
+        use super::*;
+        use insta::assert_yaml_snapshot;
+        use serde_json::json;
+        pub fn sample_question() -> Question {
+            Question {
+                id: "Вопрос 1".into(),
+                description: "Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2".into(),
+                answer: "42".into(),
+                batch_info: BatchInfo {
+                    description: "Тестовый".into(),
+                    date: "00-000-2000".into(),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }
+        }
+
+        #[test]
+        fn test_question_ser() {
+            assert_yaml_snapshot!(sample_question(), @r#"
+                ---
+                num: 0
+                id: Вопрос 1
+                description: Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2
+                answer: "42"
+                author: ""
+                comment: ""
+                comment1: ""
+                tour: ""
+                url: ""
+                date: ""
+                processed_by: ""
+                redacted_by: ""
+                copyright: ""
+                theme: ""
+                kind: ""
+                source: ""
+                rating: ""
+                batch_info:
+                  filename: ""
+                  description: Тестовый
+                  author: ""
+                  comment: ""
+                  url: ""
+                  date: 00-000-2000
+                  processed_by: ""
+                  redacted_by: ""
+                  copyright: ""
+                  theme: ""
+                  kind: ""
+                  source: ""
+                  rating: ""
+                
+                "#);
+        }
+        #[test]
+        fn test_question_de() {
+            let question_from_json: Result<Question, _> = serde_json::from_value(json!({
+                "id": "Вопрос 1",
+                "description": "Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2",
+                "answer": "42",
+                "batch_info": {
+                    "description": "Тестовый",
+                    "date": "00-000-2000"
+                }
+            }));
+            assert!(question_from_json.is_ok());
+
+            assert_yaml_snapshot!(question_from_json.unwrap(), @r#"
+            ---
+            num: 0
+            id: Вопрос 1
+            description: Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2
+            answer: "42"
+            author: ""
+            comment: ""
+            comment1: ""
+            tour: ""
+            url: ""
+            date: ""
+            processed_by: ""
+            redacted_by: ""
+            copyright: ""
+            theme: ""
+            kind: ""
+            source: ""
+            rating: ""
+            batch_info:
+              filename: ""
+              description: Тестовый
+              author: ""
+              comment: ""
+              url: ""
+              date: 00-000-2000
+              processed_by: ""
+              redacted_by: ""
+              copyright: ""
+              theme: ""
+              kind: ""
+              source: ""
+              rating: ""
+              
+            "#);
+        }
+    }
+}
+
 #[cfg(any(feature = "convert", feature = "convert_async"))]
 pub mod convert_common {
-    use super::{BatchInfo, Question};
+    use super::binary::{BatchInfo, Question};
     use crate::source::{SourceQuestion, SourceQuestionsBatch};
 
     macro_rules! make {
@@ -117,7 +307,7 @@ pub mod convert_common {
 
 #[cfg(feature = "convert")]
 pub mod convert {
-    use super::Question;
+    use super::binary::Question;
     use crate::source::SourceQuestionsBatch;
 
     pub trait QuestionsConverter {
@@ -234,7 +424,7 @@ pub mod convert_async {
     use futures_core::stream::Stream;
     use futures_util::StreamExt;
 
-    use super::Question;
+    use super::binary::Question;
     use crate::source::SourceQuestionsBatch;
 
     pub struct QuestionsConverterAsync<T>
@@ -396,7 +586,6 @@ mod test {
     use super::*;
     use insta::assert_yaml_snapshot;
     use serde_json::json;
-
     #[cfg(any(feature = "convert", feature = "convert_async"))]
     pub mod convert_common {
         use crate::source::{SourceQuestion, SourceQuestionsBatch};
@@ -442,38 +631,13 @@ mod test {
     fn test_question_ser() {
         assert_yaml_snapshot!(sample_question(), @r#"
             ---
-            num: 0
             id: Вопрос 1
             description: Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2
             answer: "42"
-            author: ""
-            comment: ""
-            comment1: ""
-            tour: ""
-            url: ""
-            date: ""
-            processed_by: ""
-            redacted_by: ""
-            copyright: ""
-            theme: ""
-            kind: ""
-            source: ""
-            rating: ""
             batch_info:
-              filename: ""
               description: Тестовый
-              author: ""
-              comment: ""
-              url: ""
               date: 00-000-2000
-              processed_by: ""
-              redacted_by: ""
-              copyright: ""
-              theme: ""
-              kind: ""
-              source: ""
-              rating: ""
-            
+              
             "#);
     }
     #[test]
@@ -491,37 +655,12 @@ mod test {
 
         assert_yaml_snapshot!(question_from_json.unwrap(), @r#"
         ---
-        num: 0
         id: Вопрос 1
         description: Сколько будет (2 * 2 * 2 + 2) * 2 * 2 + 2
         answer: "42"
-        author: ""
-        comment: ""
-        comment1: ""
-        tour: ""
-        url: ""
-        date: ""
-        processed_by: ""
-        redacted_by: ""
-        copyright: ""
-        theme: ""
-        kind: ""
-        source: ""
-        rating: ""
         batch_info:
-          filename: ""
           description: Тестовый
-          author: ""
-          comment: ""
-          url: ""
           date: 00-000-2000
-          processed_by: ""
-          redacted_by: ""
-          copyright: ""
-          theme: ""
-          kind: ""
-          source: ""
-          rating: ""
           
         "#);
     }