Repository: martinohmann/hcl-rs
Pull request #51 (2022-08-19 21:16:48)

refactor: separate serializers and formatting
With the current implementation where serializers are writing out HCL to the formatter as they go it will get complicated to get the formatting right when the full set of HCL expressions will be added to this crate. I tried multiple approaches but failed a lot.

This change adds serializers for all HCL structure types in the `structure::ser` module. The main serializer in the `ser` module just serializes everything into a `Body` which is then formatted. This allows us to emit better HCL at a later point in time since we have all the context of surrounding structures available when we start formatting. The downside of this is that serialization will be a bit slower due to the additional overhead of serializing into `Body` first, but we might be able to make up for this later by using `Cow` strings.

The `format` module will house the formatting code and eventually the `PrettyFormatter` will be updated and moved from the `ser` module into the `format` module. This will be a breaking change which is out of scope of this PR. The upside of the `format` module is that we'll be able to format the HCL types from this crate more efficiently by simply bypassing serde entirely. Convenience functions for this will be added once the `format` module becomes part of the public API.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 839bfca..d15fd30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Changelog
 
+## [0.6.5](https://github.com/martinohmann/hcl-rs/compare/v0.6.4...v0.6.5) (2022-08-19)
+
+
+### Miscellaneous
+
+* **deps:** bump actions/cache from 3.0.4 to 3.0.5 ([#48](https://github.com/martinohmann/hcl-rs/issues/48)) ([530fee4](https://github.com/martinohmann/hcl-rs/commit/530fee4f93dd4dd7b642bf1c0b719b4364166909))
+* **deps:** bump pest from 2.1.3 to 2.2.1 ([#52](https://github.com/martinohmann/hcl-rs/issues/52)) ([555b7e4](https://github.com/martinohmann/hcl-rs/commit/555b7e42a32bfcd005f2d2761d7e5a94c6704098))
+
 ## [0.6.4](https://github.com/martinohmann/hcl-rs/compare/v0.6.3...v0.6.4) (2022-07-30)
 
 
diff --git a/Cargo.toml b/Cargo.toml
index dfcdd73..eb23938 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "hcl-rs"
-version = "0.6.4"
+version = "0.6.5"
 authors = ["Martin Ohmann <martinohmann@gmail.com>"]
 license = "MIT"
 description = "An HCL deserializer"
@@ -28,8 +28,8 @@ path = "specsuite/main.rs"
 harness = false
 
 [dependencies]
-pest = "2.1.3"
-pest_derive = "2.1.0"
+pest = "2.2.1"
+pest_derive = "2.2.1"
 itoa = "1.0.1"
 ryu = "1.0.9"
 unicode-ident = "1.0.0"
diff --git a/specsuite/hcl/invalid-heredoc.hcl.json b/specsuite/hcl/invalid-heredoc.hcl.json
index 330a7bc..e9eb3b9 100644
--- a/specsuite/hcl/invalid-heredoc.hcl.json
+++ b/specsuite/hcl/invalid-heredoc.hcl.json
@@ -1,8 +1,6 @@
 {
   "message": "EOF must be immediately followed by a newline",
-  "ignore": true,
   "body": {
-    "comment": "The line feed char after the word `invalid` is present locally, somehow stop showing up in GitHub actions. Needs investigation.",
-    "message": " --> 2:12\n  |\n2 |   source = <<-EOF # This is invalid␊\n  |            ^---\n  |\n  = expected ExprTerm, Operation, or CondExpr"
+    "message": " --> 2:12\n  |\n2 |   source = <<-EOF # This is invalid\n  |            ^---\n  |\n  = expected ExprTerm, Operation, or CondExpr"
   }
 }
diff --git a/src/format/impls.rs b/src/format/impls.rs
new file mode 100644
index 0000000..c3a652d
--- /dev/null
+++ b/src/format/impls.rs
@@ -0,0 +1,252 @@
+use super::{private, Format};
+use crate::{
+    ser,
+    structure::{
+        Attribute, Block, BlockLabel, Body, Expression, Identifier, ObjectKey, RawExpression,
+        Structure,
+    },
+    Map, Number, Result, Value,
+};
+use std::io;
+
+impl private::Sealed for Body {}
+
+impl Format for Body {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        for structure in self.iter() {
+            structure.format(writer, fmt)?;
+        }
+
+        Ok(())
+    }
+}
+
+impl private::Sealed for Structure {}
+
+impl Format for Structure {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Structure::Attribute(attr) => attr.format(writer, fmt),
+            Structure::Block(block) => block.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Attribute {}
+
+impl Format for Attribute {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.begin_attribute(writer)?;
+        fmt.write_ident(writer, &self.key)?;
+        fmt.begin_attribute_value(writer)?;
+        self.expr.format(writer, fmt)?;
+        fmt.end_attribute(writer)?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for Block {}
+
+impl Format for Block {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.begin_block(writer)?;
+        fmt.write_ident(writer, &self.identifier)?;
+
+        for label in &self.labels {
+            writer.write_all(b" ")?;
+            label.format(writer, fmt)?;
+        }
+
+        fmt.begin_block_body(writer)?;
+        self.body.format(writer, fmt)?;
+        fmt.end_block(writer)?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for BlockLabel {}
+
+impl Format for BlockLabel {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            BlockLabel::Identifier(ident) => ident.format(writer, fmt),
+            BlockLabel::String(string) => string.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Expression {}
+
+impl Format for Expression {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Expression::Null => Ok(fmt.write_null(writer)?),
+            Expression::Bool(b) => Ok(fmt.write_bool(writer, *b)?),
+            Expression::Number(num) => num.format(writer, fmt),
+            Expression::String(string) => string.format(writer, fmt),
+            Expression::Array(array) => format_array(writer, fmt, array),
+            Expression::Object(object) => format_object(writer, fmt, object),
+            Expression::Raw(raw) => raw.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Value {}
+
+impl Format for Value {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Value::Null => Ok(fmt.write_null(writer)?),
+            Value::Bool(b) => Ok(fmt.write_bool(writer, *b)?),
+            Value::Number(num) => num.format(writer, fmt),
+            Value::String(string) => string.format(writer, fmt),
+            Value::Array(array) => format_array(writer, fmt, array),
+            Value::Object(object) => format_object(writer, fmt, object),
+        }
+    }
+}
+
+impl private::Sealed for Number {}
+
+impl Format for Number {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match *self {
+            Number::PosInt(pos) => fmt.write_int(writer, pos)?,
+            Number::NegInt(neg) => fmt.write_int(writer, neg)?,
+            Number::Float(float) => fmt.write_float(writer, float)?,
+        }
+
+        Ok(())
+    }
+}
+
+impl private::Sealed for ObjectKey {}
+
+impl Format for ObjectKey {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            ObjectKey::Identifier(ident) => ident.format(writer, fmt),
+            ObjectKey::String(string) => string.format(writer, fmt),
+            ObjectKey::RawExpression(raw) => {
+                fmt.begin_interpolated_string(writer)?;
+                raw.format(writer, fmt)?;
+                fmt.end_interpolated_string(writer)?;
+                Ok(())
+            }
+        }
+    }
+}
+
+impl private::Sealed for RawExpression {}
+
+impl Format for RawExpression {
+    fn format<W, F>(&self, writer: &mut W, _: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        writer.write_all(self.as_str().as_bytes())?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for Identifier {}
+
+impl Format for Identifier {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.write_ident(writer, self.as_str())?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for String {}
+
+impl Format for String {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.write_quoted_string(writer, self)?;
+        Ok(())
+    }
+}
+
+fn format_array<W, F, T>(writer: &mut W, fmt: &mut F, array: &[T]) -> Result<()>
+where
+    W: ?Sized + io::Write,
+    F: ?Sized + ser::Format,
+    T: Format,
+{
+    fmt.begin_array(writer)?;
+
+    for value in array {
+        fmt.begin_array_value(writer)?;
+        value.format(writer, fmt)?;
+        fmt.end_array_value(writer)?;
+    }
+
+    fmt.end_array(writer)?;
+    Ok(())
+}
+
+fn format_object<W, F, K, V>(writer: &mut W, fmt: &mut F, object: &Map<K, V>) -> Result<()>
+where
+    W: ?Sized + io::Write,
+    F: ?Sized + ser::Format,
+    K: Format,
+    V: Format,
+{
+    fmt.begin_object(writer)?;
+
+    for (key, value) in object {
+        fmt.begin_object_key(writer)?;
+        key.format(writer, fmt)?;
+        fmt.begin_object_value(writer)?;
+        value.format(writer, fmt)?;
+        fmt.end_object_value(writer)?;
+    }
+
+    fmt.end_object(writer)?;
+    Ok(())
+}
diff --git a/src/format/mod.rs b/src/format/mod.rs
new file mode 100644
index 0000000..85f6eb0
--- /dev/null
+++ b/src/format/mod.rs
@@ -0,0 +1,29 @@
+//! Contains the logic to format HCL data structure.
+
+// @NOTE(mohmann): This module is not exported yet since it is subject to change due to a bigger
+// multi-step refactoring. It will eventually replace the formatting code inside serializer
+// module.
+
+mod impls;
+
+use crate::{ser, Result};
+use std::io;
+
+mod private {
+    pub trait Sealed {}
+}
+
+/// A trait to format data structures as HCL.
+///
+/// This trait is sealed to prevent implementation outside of this crate.
+pub trait Format: private::Sealed {
+    /// Formats a HCL structure using a formatter and writes the result to the provided writer.
+    ///
+    /// ## Errors
+    ///
+    /// Formatting the data structure or writing to the writer may fail with an `Error`.
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format;
+}
diff --git a/src/lib.rs b/src/lib.rs
index 23b33bc..910892e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,7 @@ pub mod de;
 pub mod error;
 #[macro_use]
 mod macros;
+mod format;
 mod number;
 mod parser;
 pub mod ser;
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 2f6a787..17b39cd 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -192,8 +192,14 @@ mod format;
 mod tests;
 
 pub use self::format::{Format, PrettyFormatter, PrettyFormatterBuilder};
-use crate::{Error, Result};
-use serde::ser::{self, Impossible, Serialize, SerializeSeq};
+use crate::{
+    structure::ser::body::{
+        BodySerializer, SerializeBodyMap, SerializeBodySeq, SerializeBodyStruct,
+        SerializeBodyStructVariant, SerializeBodyTupleVariant,
+    },
+    Error, Result,
+};
+use serde::ser::{self, Serialize};
 use std::io;
 
 /// A structure for serializing Rust values into HCL.
@@ -228,68 +234,11 @@ where
         self.writer
     }
 
-    fn serialize_attribute<K, V>(&mut self, key: &K, value: &V) -> Result<()>
+    fn format<V>(&mut self, value: V) -> Result<()>
     where
-        K: ?Sized + Serialize,
-        V: ?Sized + Serialize,
+        V: crate::format::Format,
     {
-        self.serialize_attribute_key(key)?;
-        self.serialize_attribute_value(value)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn serialize_attribute_key<K>(&mut self, key: &K) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-    {
-        self.formatter.begin_attribute(&mut self.writer)?;
-        key.serialize(IdentifierSerializer::new(self))
-    }
-
-    fn serialize_attribute_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))
-    }
-
-    fn serialize_array_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_array_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))?;
-        self.formatter.end_array_value(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn serialize_object_key_value<K, V>(&mut self, key: &K, value: &V) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-        V: ?Sized + Serialize,
-    {
-        self.serialize_object_key(key)?;
-        self.serialize_object_value(value)
-    }
-
-    fn serialize_object_key<K>(&mut self, key: &K) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-    {
-        self.formatter.begin_object_key(&mut self.writer)?;
-        key.serialize(ObjectKeySerializer::new(self))
-    }
-
-    fn serialize_object_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_object_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))?;
-        self.formatter.end_object_value(&mut self.writer)?;
-        Ok(())
+        value.format(&mut self.writer, &mut self.formatter)
     }
 }
 
@@ -301,13 +250,13 @@ where
     type Ok = ();
     type Error = Error;
 
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Self;
-    type SerializeTupleVariant = Self;
-    type SerializeMap = Self;
+    type SerializeSeq = SerializeSeq<'a, W, F>;
+    type SerializeTuple = SerializeSeq<'a, W, F>;
+    type SerializeTupleStruct = SerializeSeq<'a, W, F>;
+    type SerializeTupleVariant = SerializeTupleVariant<'a, W, F>;
+    type SerializeMap = SerializeMap<'a, W, F>;
     type SerializeStruct = SerializeStruct<'a, W, F>;
-    type SerializeStructVariant = Self;
+    type SerializeStructVariant = SerializeStructVariant<'a, W, F>;
 
     serialize_unsupported! {
         bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
@@ -318,49 +267,45 @@ where
     where
         T: ?Sized + Serialize,
     {
-        value.serialize(self)
+        BodySerializer
+            .serialize_some(value)
+            .and_then(|body| self.format(body))
     }
 
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
+    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        value.serialize(self)
+        BodySerializer
+            .serialize_newtype_struct(name, value)
+            .and_then(|body| self.format(body))
     }
 
-    /// Newtype variants have special handling for `hcl::Structure`. For this enum, the inner type
-    /// is serialized, which is either `hcl::Attribute` or `hcl::Block`. These will be handled by
-    /// `serialize_struct` below.
-    ///
-    /// Any other newtype variant is serialized as an HCL attribute (`VARIANT = VALUE`)
     fn serialize_newtype_variant<T>(
         self,
         name: &'static str,
-        _variant_index: u32,
+        variant_index: u32,
         variant: &'static str,
         value: &T,
     ) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        if name == "$hcl::structure" {
-            value.serialize(self)
-        } else {
-            self.serialize_attribute(variant, value)
-        }
+        BodySerializer
+            .serialize_newtype_variant(name, variant_index, variant, value)
+            .and_then(|body| self.format(body))
     }
 
-    /// A sequence of HCL attributes and blocks.
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(self)
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        BodySerializer
+            .serialize_seq(len)
+            .map(|inner| SerializeSeq { ser: self, inner })
     }
 
-    /// A tuple of HCL attributes and blocks.
     fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
         self.serialize_seq(Some(len))
     }
 
-    /// A tuple of HCL attributes and blocks.
     fn serialize_tuple_struct(
         self,
         _name: &'static str,
@@ -369,76 +314,50 @@ where
         self.serialize_seq(Some(len))
     }
 
-    /// Tuple variants are serialized as HCL attributes with an array value (`VARIANT = [...]`).
     fn serialize_tuple_variant(
         self,
-        _name: &'static str,
-        _variant_index: u32,
+        name: &'static str,
+        variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeTupleVariant> {
-        self.serialize_attribute_key(variant)?;
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        self.formatter.begin_array(&mut self.writer)?;
-        Ok(self)
+        BodySerializer
+            .serialize_tuple_variant(name, variant_index, variant, len)
+            .map(|inner| SerializeTupleVariant { ser: self, inner })
     }
 
-    /// Maps are serialized as sequences of HCL attributes (`KEY1 = VALUE1`).
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Ok(self)
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        BodySerializer
+            .serialize_map(len)
+            .map(|inner| SerializeMap { ser: self, inner })
     }
 
-    /// Structs have special handling for `hcl::Attribute` and `hcl::Block`. Attributes are
-    /// serialized as key-expression pairs (`KEY = EXPR`), whereas blocks are serialized as block
-    /// identifier, block labels (if any) and block body.
-    ///
-    /// Any other struct is serialized as a sequence of HCL attributes.
-    fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        let kind = match name {
-            "$hcl::attribute" => StructKind::Attribute,
-            "$hcl::block" => StructKind::Block,
-            _ => StructKind::Custom,
-        };
-
-        Ok(SerializeStruct::new(kind, self))
+    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        BodySerializer
+            .serialize_struct(name, len)
+            .map(|inner| SerializeStruct { ser: self, inner })
     }
 
-    /// Struct variants are serialized as HCL attributes with object value (`VARIANT = {...}`).
     fn serialize_struct_variant(
         self,
-        _name: &'static str,
-        _variant_index: u32,
+        name: &'static str,
+        variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant> {
-        self.serialize_attribute_key(variant)?;
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        self.formatter.begin_object(&mut self.writer)?;
-        Ok(self)
+        BodySerializer
+            .serialize_struct_variant(name, variant_index, variant, len)
+            .map(|inner| SerializeStructVariant { ser: self, inner })
     }
 }
 
-impl<'a, W, F> ser::SerializeSeq for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(&mut **self)
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeSeq<'a, W, F> {
+    inner: SerializeBodySeq,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeTuple for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeSeq for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -450,15 +369,15 @@ where
     where
         T: ?Sized + Serialize,
     {
-        ser::SerializeSeq::serialize_element(self, value)
+        self.inner.serialize_element(value)
     }
 
     fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleStruct for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeTuple for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -466,7 +385,7 @@ where
     type Ok = ();
     type Error = Error;
 
-    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
@@ -478,7 +397,7 @@ where
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleVariant for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeTupleStruct for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -487,403 +406,6 @@ where
     type Error = Error;
 
     fn serialize_field<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_array_value(value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.formatter.end_array(&mut self.writer)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeMap for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute_key(key)
-    }
-
-    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute_value(value)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeStruct for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeStructVariant for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.formatter.end_object(&mut self.writer)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-}
-
-struct IdentifierSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> IdentifierSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> IdentifierSerializer<'a, W, F> {
-        IdentifierSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for IdentifierSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant newtype_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser.formatter.write_ident(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-}
-
-struct RawExpressionSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> RawExpressionSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> RawExpressionSerializer<'a, W, F> {
-        RawExpressionSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for RawExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant newtype_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser.writer.write_all(v.as_bytes())?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-}
-
-struct ObjectKeySerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> ObjectKeySerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> ObjectKeySerializer<'a, W, F> {
-        ObjectKeySerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for ObjectKeySerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        _variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        match name {
-            "$hcl::identifier" => value.serialize(IdentifierSerializer::new(self.ser)),
-            "$hcl::raw_expression" => {
-                self.ser
-                    .formatter
-                    .begin_interpolated_string(&mut self.ser.writer)?;
-                value.serialize(RawExpressionSerializer::new(self.ser))?;
-                self.ser
-                    .formatter
-                    .end_interpolated_string(&mut self.ser.writer)?;
-                Ok(())
-            }
-            _ => value.serialize(self),
-        }
-    }
-}
-
-struct BlockLabelSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> BlockLabelSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> BlockLabelSerializer<'a, W, F> {
-        BlockLabelSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant
-        tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        _variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::identifier" {
-            value.serialize(IdentifierSerializer::new(self.ser))
-        } else {
-            value.serialize(self)
-        }
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(self)
-    }
-
-    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
-        self.serialize_seq(Some(len))
-    }
-}
-
-impl<'a, W, F> ser::SerializeSeq for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.writer.write_all(b" ")?;
-        value.serialize(BlockLabelSerializer::new(self.ser))
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeTuple for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
@@ -895,260 +417,13 @@ where
     }
 }
 
-struct ExpressionSerializer<'a, W: 'a, F: 'a> {
+#[doc(hidden)]
+pub struct SerializeTupleVariant<'a, W, F> {
+    inner: SerializeBodyTupleVariant,
     ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ExpressionSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> ExpressionSerializer<'a, W, F> {
-        ExpressionSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Self;
-    type SerializeTupleVariant = Self;
-    type SerializeMap = Self;
-    type SerializeStruct = Self;
-    type SerializeStructVariant = Self;
-
-    fn serialize_bool(self, v: bool) -> Result<()> {
-        self.ser.formatter.write_bool(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_i8(self, v: i8) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i16(self, v: i16) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i32(self, v: i32) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i64(self, v: i64) -> Result<()> {
-        self.ser.formatter.write_int(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_u8(self, v: u8) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u16(self, v: u16) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u32(self, v: u32) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u64(self, v: u64) -> Result<()> {
-        self.ser.formatter.write_int(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_f32(self, v: f32) -> Result<()> {
-        self.serialize_f64(f64::from(v))
-    }
-
-    fn serialize_f64(self, v: f64) -> Result<()> {
-        self.ser.formatter.write_float(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
-        let mut seq = self.serialize_seq(Some(v.len()))?;
-        for byte in v {
-            seq.serialize_element(byte)?;
-        }
-        seq.end()
-    }
-
-    fn serialize_none(self) -> Result<()> {
-        self.serialize_unit()
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_unit(self) -> Result<()> {
-        self.ser.formatter.write_null(&mut self.ser.writer)?;
-        Ok(())
-    }
-
-    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
-        self.serialize_unit()
-    }
-
-    fn serialize_unit_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<()> {
-        self.serialize_str(variant)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::raw_expression" {
-            value.serialize(RawExpressionSerializer::new(self.ser))
-        } else {
-            value.serialize(self)
-        }
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::expression" {
-            value.serialize(self)
-        } else {
-            self.ser.formatter.begin_object(&mut self.ser.writer)?;
-            self.ser.serialize_object_key_value(variant, value)?;
-            self.ser.formatter.end_object(&mut self.ser.writer)?;
-            Ok(())
-        }
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        self.ser.formatter.begin_array(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
-        self.serialize_seq(Some(len))
-    }
-
-    fn serialize_tuple_struct(
-        self,
-        _name: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleStruct> {
-        self.serialize_seq(Some(len))
-    }
-
-    fn serialize_tuple_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        _len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        self.ser.serialize_object_key(variant)?;
-        self.ser
-            .formatter
-            .begin_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.begin_array(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
-        self.serialize_map(Some(len))
-    }
-
-    fn serialize_struct_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        _len: usize,
-    ) -> Result<Self::SerializeStructVariant> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        self.ser.serialize_object_key(variant)?;
-        self.ser
-            .formatter
-            .begin_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        Ok(self)
-    }
-}
-
-impl<'a, W, F> ser::SerializeSeq for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.serialize_array_value(value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_array(&mut self.ser.writer)?;
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeTuple for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
-    }
-}
-
-impl<'a, W, F> ser::SerializeTupleStruct for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeTupleVariant for SerializeTupleVariant<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1160,38 +435,21 @@ where
     where
         T: ?Sized + Serialize,
     {
-        ser::SerializeSeq::serialize_element(self, value)
+        self.inner.serialize_field(value)
     }
 
     fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleVariant for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_array(&mut self.ser.writer)?;
-        self.ser.formatter.end_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeMap<'a, W, F> {
+    inner: SerializeBodyMap,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeMap for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeMap for SerializeMap<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1203,44 +461,28 @@ where
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_key(key)
+        self.inner.serialize_key(key)
     }
 
     fn serialize_value<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_value(value)
+        self.inner.serialize_value(value)
     }
 
     fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeStruct for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.serialize_object_key_value(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeStructVariant<'a, W, F> {
+    inner: SerializeBodyStructVariant,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeStructVariant for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeStructVariant for SerializeStructVariant<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1252,35 +494,20 @@ where
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_key_value(key, value)
+        self.inner.serialize_field(key, value)
     }
 
     fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        self.ser.formatter.end_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-enum StructKind {
-    Attribute,
-    Block,
-    Custom,
-}
-
 #[doc(hidden)]
 pub struct SerializeStruct<'a, W, F> {
-    kind: StructKind,
+    inner: SerializeBodyStruct,
     ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> SerializeStruct<'a, W, F> {
-    fn new(kind: StructKind, ser: &'a mut Serializer<W, F>) -> Self {
-        SerializeStruct { kind, ser }
-    }
-}
-
 impl<'a, W, F> ser::SerializeStruct for SerializeStruct<'a, W, F>
 where
     W: io::Write,
@@ -1293,36 +520,11 @@ where
     where
         T: ?Sized + Serialize,
     {
-        match self.kind {
-            StructKind::Attribute => match key {
-                "key" => self.ser.serialize_attribute_key(value),
-                "expr" => {
-                    self.ser.serialize_attribute_value(value)?;
-                    self.ser.formatter.end_attribute(&mut self.ser.writer)?;
-                    Ok(())
-                }
-                _ => Ok(()),
-            },
-            StructKind::Block => match key {
-                "identifier" => {
-                    self.ser.formatter.begin_block(&mut self.ser.writer)?;
-                    value.serialize(IdentifierSerializer::new(self.ser))
-                }
-                "labels" => value.serialize(BlockLabelSerializer::new(self.ser)),
-                "body" => {
-                    self.ser.formatter.begin_block_body(&mut self.ser.writer)?;
-                    value.serialize(&mut *self.ser)?;
-                    self.ser.formatter.end_block(&mut self.ser.writer)?;
-                    Ok(())
-                }
-                _ => Ok(()),
-            },
-            StructKind::Custom => self.ser.serialize_attribute(key, value),
-        }
+        self.inner.serialize_field(key, value)
     }
 
     fn end(self) -> Result<()> {
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
diff --git a/src/structure/body.rs b/src/structure/body.rs
index 6a7dc6d..0179bdc 100644
--- a/src/structure/body.rs
+++ b/src/structure/body.rs
@@ -193,6 +193,15 @@ impl From<Body> for Map<String, Value> {
     }
 }
 
+impl<T> From<T> for Body
+where
+    T: Into<Structure>,
+{
+    fn from(value: T) -> Body {
+        Body(vec![value.into()])
+    }
+}
+
 impl<S> FromIterator<S> for Body
 where
     S: Into<Structure>,
diff --git a/src/structure/expression.rs b/src/structure/expression.rs
index 204e068..eb1b525 100644
--- a/src/structure/expression.rs
+++ b/src/structure/expression.rs
@@ -253,6 +253,12 @@ impl RawExpression {
     pub fn into_inner(self) -> String {
         self.0
     }
+
+    /// Returns the `RawExpression` as a `&str`. If you want to represent the `RawExpression` as
+    /// an interpolated string, use `.to_string()` instead.
+    pub fn as_str(&self) -> &str {
+        &self.0
+    }
 }
 
 impl From<String> for RawExpression {
diff --git a/src/structure/ser/attribute.rs b/src/structure/ser/attribute.rs
new file mode 100644
index 0000000..ade2e6a
--- /dev/null
+++ b/src/structure/ser/attribute.rs
@@ -0,0 +1,329 @@
+use super::{expression::ExpressionSerializer, StringSerializer};
+use crate::{serialize_unsupported, Attribute, Error, Expression, Object, ObjectKey, Result};
+use serde::ser::{self, Serialize};
+
+pub struct AttributeSerializer;
+
+impl ser::Serializer for AttributeSerializer {
+    type Ok = Attribute;
+    type Error = Error;
+
+    type SerializeSeq = SerializeAttributeSeq;
+    type SerializeTuple = SerializeAttributeSeq;
+    type SerializeTupleStruct = SerializeAttributeSeq;
+    type SerializeTupleVariant = SerializeAttributeTupleVariant;
+    type SerializeMap = SerializeAttributeMap;
+    type SerializeStruct = SerializeAttributeStruct;
+    type SerializeStructVariant = SerializeAttributeStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        Ok(Attribute::new(
+            variant,
+            value.serialize(ExpressionSerializer)?,
+        ))
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeAttributeSeq::new())
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeAttributeTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeAttributeMap::new())
+    }
+
+    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeAttributeStruct::new())
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeAttributeStructVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeAttributeSeq {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeSeq {
+    pub fn new() -> Self {
+        SerializeAttributeSeq {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            self.key = Some(value.serialize(StringSerializer)?);
+        } else if self.expr.is_none() {
+            self.expr = Some(value.serialize(ExpressionSerializer)?);
+        } else {
+            return Err(ser::Error::custom("expected sequence with 2 elements"));
+        }
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (_, _) => Err(ser::Error::custom("expected sequence with 2 elements")),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeAttributeTupleVariant {
+    key: String,
+    vec: Vec<Expression>,
+}
+
+impl SerializeAttributeTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeAttributeTupleVariant {
+            key: variant.to_owned(),
+            vec: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeAttributeTupleVariant {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.key, self.vec))
+    }
+}
+
+pub struct SerializeAttributeMap {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeMap {
+    pub fn new() -> Self {
+        SerializeAttributeMap {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeAttributeMap {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            self.key = Some(key.serialize(StringSerializer)?);
+            Ok(())
+        } else {
+            Err(ser::Error::custom("expected map with 1 entry"))
+        }
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            panic!("serialize_value called before serialize_key");
+        }
+
+        self.expr = Some(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (Some(_), None) => Err(ser::Error::custom("attribute value missing")),
+            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
+        }
+    }
+}
+
+pub struct SerializeAttributeStruct {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeStruct {
+    pub fn new() -> Self {
+        SerializeAttributeStruct {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeAttributeStruct {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match key {
+            "key" => self.key = Some(value.serialize(StringSerializer)?),
+            "expr" => self.expr = Some(value.serialize(ExpressionSerializer)?),
+            _ => {
+                return Err(ser::Error::custom(
+                    "expected struct with fields `key` and `expr`",
+                ))
+            }
+        };
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (_, _) => Err(ser::Error::custom(
+                "expected struct with fields `key` and `expr`",
+            )),
+        }
+    }
+}
+
+pub struct SerializeAttributeStructVariant {
+    key: String,
+    map: Object<ObjectKey, Expression>,
+}
+
+impl SerializeAttributeStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeAttributeStructVariant {
+            key: variant.to_owned(),
+            map: Object::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeAttributeStructVariant {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(ObjectKey::identifier(key), expr);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.key, self.map))
+    }
+}
diff --git a/src/structure/ser/block.rs b/src/structure/ser/block.rs
new file mode 100644
index 0000000..adcab4e
--- /dev/null
+++ b/src/structure/ser/block.rs
@@ -0,0 +1,440 @@
+use super::{
+    body::BodySerializer, expression::ExpressionSerializer, structure::StructureSerializer,
+    IdentifierSerializer, SeqSerializer, StringSerializer,
+};
+use crate::{serialize_unsupported, Attribute, Block, BlockLabel, Body, Error, Result, Structure};
+use serde::ser::{self, Impossible, Serialize};
+use std::fmt::Display;
+
+pub struct BlockSerializer;
+
+impl ser::Serializer for BlockSerializer {
+    type Ok = Block;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBlockSeq;
+    type SerializeTuple = SerializeBlockSeq;
+    type SerializeTupleStruct = SerializeBlockSeq;
+    type SerializeTupleVariant = SerializeBlockVariant;
+    type SerializeMap = SerializeBlockMap;
+    type SerializeStruct = SerializeBlockStruct;
+    type SerializeStructVariant = SerializeBlockVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        Ok(Block {
+            identifier: variant.to_owned(),
+            labels: Vec::new(),
+            body: value.serialize(BodySerializer)?,
+        })
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBlockSeq::new())
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeBlockVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeBlockMap::new())
+    }
+
+    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeBlockStruct::new())
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeBlockVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeBlockSeq {
+    identifier: Option<String>,
+    labels: Option<Vec<BlockLabel>>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockSeq {
+    pub fn new() -> Self {
+        SerializeBlockSeq {
+            identifier: None,
+            labels: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            self.identifier = Some(value.serialize(StringSerializer)?);
+        } else if self.labels.is_none() {
+            self.labels = Some(value.serialize(SeqSerializer::new(BlockLabelSerializer))?);
+        } else if self.body.is_none() {
+            self.body = Some(value.serialize(BodySerializer)?);
+        } else {
+            return Err(ser::Error::custom("expected sequence with 3 elements"));
+        }
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.labels, self.body) {
+            (Some(ident), Some(labels), Some(body)) => Ok(Block::new(ident, labels, body)),
+            (_, _, _) => Err(ser::Error::custom("expected sequence with 3 elements")),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeBlockVariant {
+    identifier: String,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBlockVariant {
+            identifier: variant.to_owned(),
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeBlockVariant {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.structures.push(value.serialize(StructureSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeStructVariant::end(self)
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeBlockVariant {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.structures.push(Attribute::new(key, expr).into());
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block {
+            identifier: self.identifier,
+            labels: Vec::new(),
+            body: Body(self.structures),
+        })
+    }
+}
+
+pub struct SerializeBlockMap {
+    identifier: Option<String>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockMap {
+    pub fn new() -> Self {
+        SerializeBlockMap {
+            identifier: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeBlockMap {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            self.identifier = Some(key.serialize(StringSerializer)?);
+            Ok(())
+        } else {
+            Err(ser::Error::custom("expected map with 1 entry"))
+        }
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            panic!("serialize_value called before serialize_key");
+        }
+
+        self.body = Some(value.serialize(BodySerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.body) {
+            (Some(ident), Some(body)) => Ok(Block::new(ident, Vec::<BlockLabel>::new(), body)),
+            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
+        }
+    }
+}
+
+pub struct SerializeBlockStruct {
+    identifier: Option<String>,
+    labels: Option<Vec<BlockLabel>>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockStruct {
+    pub fn new() -> Self {
+        SerializeBlockStruct {
+            identifier: None,
+            labels: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeBlockStruct {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match key {
+            "identifier" => self.identifier = Some(value.serialize(StringSerializer)?),
+            "labels" => {
+                self.labels = Some(value.serialize(SeqSerializer::new(BlockLabelSerializer))?)
+            }
+            "body" => self.body = Some(value.serialize(BodySerializer)?),
+            _ => {
+                return Err(ser::Error::custom(
+                    "expected struct with fields `identifier`, `body` and optional `labels`",
+                ))
+            }
+        };
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.body) {
+            (Some(ident), Some(body)) => {
+                Ok(Block::new(ident, self.labels.unwrap_or_default(), body))
+            }
+            (_, _) => Err(ser::Error::custom(
+                "expected struct with fields `identifier`, `body` and optional `labels`",
+            )),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct BlockLabelSerializer;
+
+impl ser::Serializer for BlockLabelSerializer {
+    type Ok = BlockLabel;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<BlockLabel, Error>;
+    type SerializeTuple = Impossible<BlockLabel, Error>;
+    type SerializeTupleStruct = Impossible<BlockLabel, Error>;
+    type SerializeTupleVariant = Impossible<BlockLabel, Error>;
+    type SerializeMap = Impossible<BlockLabel, Error>;
+    type SerializeStruct = Impossible<BlockLabel, Error>;
+    type SerializeStructVariant = Impossible<BlockLabel, Error>;
+
+    serialize_unsupported! {
+        bool f32 f64 bytes unit unit_struct none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(BlockLabel::string(value))
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(BlockLabel::identifier(variant))
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        // Specialization for the `BlockLabel` type itself.
+        match (name, variant) {
+            ("$hcl::block_label", "Identifier") => Ok(BlockLabel::Identifier(
+                value.serialize(IdentifierSerializer)?,
+            )),
+            ("$hcl::block_label", "String") => {
+                Ok(BlockLabel::String(value.serialize(StringSerializer)?))
+            }
+            (_, _) => value.serialize(self),
+        }
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+}
diff --git a/src/structure/ser/body.rs b/src/structure/ser/body.rs
new file mode 100644
index 0000000..b55da88
--- /dev/null
+++ b/src/structure/ser/body.rs
@@ -0,0 +1,306 @@
+use super::{
+    attribute::SerializeAttributeStruct,
+    block::SerializeBlockStruct,
+    expression::ExpressionSerializer,
+    structure::{
+        SerializeStructureStructVariant, SerializeStructureTupleVariant, StructureSerializer,
+    },
+    IdentifierSerializer,
+};
+use crate::{serialize_unsupported, Attribute, Body, Error, Result, Structure};
+use serde::ser::{self, Serialize, SerializeMap};
+
+pub struct BodySerializer;
+
+impl ser::Serializer for BodySerializer {
+    type Ok = Body;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBodySeq;
+    type SerializeTuple = SerializeBodySeq;
+    type SerializeTupleStruct = SerializeBodySeq;
+    type SerializeTupleVariant = SerializeBodyTupleVariant;
+    type SerializeMap = SerializeBodyMap;
+    type SerializeStruct = SerializeBodyStruct;
+    type SerializeStructVariant = SerializeBodyStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        match name {
+            "$hcl::body" => value.serialize(self),
+            _ => StructureSerializer
+                .serialize_newtype_variant(name, variant_index, variant, value)
+                .map(Into::into),
+        }
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBodySeq::new(len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeBodyTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeBodyMap::new(len))
+    }
+
+    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeBodyStruct::new(name, len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeBodyStructVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeBodySeq {
+    vec: Vec<Structure>,
+}
+
+impl SerializeBodySeq {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeBodySeq {
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(StructureSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Body(self.vec))
+    }
+}
+
+impl ser::SerializeTuple for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeBodyTupleVariant {
+    inner: SerializeStructureTupleVariant,
+}
+
+impl SerializeBodyTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBodyTupleVariant {
+            inner: SerializeStructureTupleVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeBodyTupleVariant {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub struct SerializeBodyMap {
+    structures: Vec<Structure>,
+    next_key: Option<String>,
+}
+
+impl SerializeBodyMap {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeBodyMap {
+            structures: Vec::with_capacity(len.unwrap_or(0)),
+            next_key: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeBodyMap {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.next_key = Some(key.serialize(IdentifierSerializer)?.into_inner());
+        Ok(())
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let key = self.next_key.take();
+        let key = key.expect("serialize_value called before serialize_key");
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.structures.push(Attribute::new(key, expr).into());
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Body(self.structures))
+    }
+}
+
+pub enum SerializeBodyStruct {
+    Attribute(SerializeAttributeStruct),
+    Block(SerializeBlockStruct),
+    Other(SerializeBodyMap),
+}
+
+impl SerializeBodyStruct {
+    pub fn new(name: &'static str, len: usize) -> Self {
+        match name {
+            "$hcl::attribute" => SerializeBodyStruct::Attribute(SerializeAttributeStruct::new()),
+            "$hcl::block" => SerializeBodyStruct::Block(SerializeBlockStruct::new()),
+            _ => SerializeBodyStruct::Other(SerializeBodyMap::new(Some(len))),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeBodyStruct {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        match self {
+            SerializeBodyStruct::Attribute(ser) => ser.serialize_field(key, value),
+            SerializeBodyStruct::Block(ser) => ser.serialize_field(key, value),
+            SerializeBodyStruct::Other(ser) => ser.serialize_entry(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Body> {
+        match self {
+            SerializeBodyStruct::Attribute(ser) => ser.end().map(Into::into),
+            SerializeBodyStruct::Block(ser) => ser.end().map(Into::into),
+            SerializeBodyStruct::Other(ser) => ser.end(),
+        }
+    }
+}
+
+pub struct SerializeBodyStructVariant {
+    inner: SerializeStructureStructVariant,
+}
+
+impl SerializeBodyStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBodyStructVariant {
+            inner: SerializeStructureStructVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeBodyStructVariant {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(key, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
diff --git a/src/structure/ser.rs b/src/structure/ser/expression.rs
similarity index 77%
rename from src/structure/ser.rs
rename to src/structure/ser/expression.rs
index 35edf58..9a91b50 100644
--- a/src/structure/ser.rs
+++ b/src/structure/ser/expression.rs
@@ -1,3 +1,4 @@
+use super::{IdentifierSerializer, StringSerializer};
 use crate::{serialize_unsupported, Error, Expression, Object, ObjectKey, Result};
 use serde::ser::{self, Impossible};
 use std::fmt::Display;
@@ -93,16 +94,20 @@ impl ser::Serializer for ExpressionSerializer {
         self.serialize_str(variant)
     }
 
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
     where
         T: ?Sized + ser::Serialize,
     {
-        value.serialize(self)
+        if name == "$hcl::raw_expression" {
+            Ok(Expression::Raw(value.serialize(StringSerializer)?.into()))
+        } else {
+            value.serialize(self)
+        }
     }
 
     fn serialize_newtype_variant<T>(
         self,
-        _name: &'static str,
+        name: &'static str,
         _variant_index: u32,
         variant: &'static str,
         value: &T,
@@ -110,9 +115,13 @@ impl ser::Serializer for ExpressionSerializer {
     where
         T: ?Sized + ser::Serialize,
     {
-        let mut object = Object::new();
-        object.insert(ObjectKey::string(variant), to_expression(&value)?);
-        Ok(Expression::Object(object))
+        if name == "$hcl::expression" {
+            value.serialize(self)
+        } else {
+            let mut object = Object::new();
+            object.insert(ObjectKey::identifier(variant), value.serialize(self)?);
+            Ok(Expression::Object(object))
+        }
     }
 
     fn serialize_none(self) -> Result<Self::Ok> {
@@ -127,9 +136,7 @@ impl ser::Serializer for ExpressionSerializer {
     }
 
     fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeExpressionSeq {
-            vec: Vec::with_capacity(len.unwrap_or(0)),
-        })
+        Ok(SerializeExpressionSeq::new(len))
     }
 
     fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
@@ -151,17 +158,11 @@ impl ser::Serializer for ExpressionSerializer {
         variant: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleVariant> {
-        Ok(SerializeExpressionTupleVariant {
-            name: variant.to_owned(),
-            vec: Vec::with_capacity(len),
-        })
+        Ok(SerializeExpressionTupleVariant::new(variant, len))
     }
 
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Ok(SerializeExpressionMap {
-            map: Object::new(),
-            next_key: None,
-        })
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeExpressionMap::new(len))
     }
 
     fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
@@ -173,12 +174,9 @@ impl ser::Serializer for ExpressionSerializer {
         _name: &'static str,
         _variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant> {
-        Ok(SerializeExpressionStructVariant {
-            name: variant.to_owned(),
-            map: Object::new(),
-        })
+        Ok(SerializeExpressionStructVariant::new(variant, len))
     }
 
     fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
@@ -188,10 +186,19 @@ impl ser::Serializer for ExpressionSerializer {
         Ok(Expression::String(value.to_string()))
     }
 }
+
 pub struct SerializeExpressionSeq {
     vec: Vec<Expression>,
 }
 
+impl SerializeExpressionSeq {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeExpressionSeq {
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
 impl ser::SerializeSeq for SerializeExpressionSeq {
     type Ok = Expression;
     type Error = Error;
@@ -200,7 +207,7 @@ impl ser::SerializeSeq for SerializeExpressionSeq {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(to_expression(&value)?);
+        self.vec.push(value.serialize(ExpressionSerializer)?);
         Ok(())
     }
 
@@ -242,10 +249,19 @@ impl serde::ser::SerializeTupleStruct for SerializeExpressionSeq {
 }
 
 pub struct SerializeExpressionTupleVariant {
-    name: String,
+    name: ObjectKey,
     vec: Vec<Expression>,
 }
 
+impl SerializeExpressionTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeExpressionTupleVariant {
+            name: ObjectKey::from(variant),
+            vec: Vec::with_capacity(len),
+        }
+    }
+}
+
 impl ser::SerializeTupleVariant for SerializeExpressionTupleVariant {
     type Ok = Expression;
     type Error = Error;
@@ -254,13 +270,13 @@ impl ser::SerializeTupleVariant for SerializeExpressionTupleVariant {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(to_expression(&value)?);
+        self.vec.push(value.serialize(ExpressionSerializer)?);
         Ok(())
     }
 
     fn end(self) -> Result<Self::Ok> {
         let mut object = Object::new();
-        object.insert(ObjectKey::String(self.name), Expression::Array(self.vec));
+        object.insert(self.name, self.vec.into());
         Ok(Expression::Object(object))
     }
 }
@@ -270,6 +286,15 @@ pub struct SerializeExpressionMap {
     next_key: Option<ObjectKey>,
 }
 
+impl SerializeExpressionMap {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeExpressionMap {
+            map: Object::with_capacity(len.unwrap_or(0)),
+            next_key: None,
+        }
+    }
+}
+
 impl ser::SerializeMap for SerializeExpressionMap {
     type Ok = Expression;
     type Error = Error;
@@ -288,7 +313,8 @@ impl ser::SerializeMap for SerializeExpressionMap {
     {
         let key = self.next_key.take();
         let key = key.expect("serialize_value called before serialize_key");
-        self.map.insert(key, to_expression(&value)?);
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(key, expr);
         Ok(())
     }
 
@@ -314,10 +340,19 @@ impl ser::SerializeStruct for SerializeExpressionMap {
 }
 
 pub struct SerializeExpressionStructVariant {
-    name: String,
+    name: ObjectKey,
     map: Object<ObjectKey, Expression>,
 }
 
+impl SerializeExpressionStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeExpressionStructVariant {
+            name: ObjectKey::from(variant),
+            map: Object::with_capacity(len),
+        }
+    }
+}
+
 impl ser::SerializeStructVariant for SerializeExpressionStructVariant {
     type Ok = Expression;
     type Error = Error;
@@ -326,19 +361,19 @@ impl ser::SerializeStructVariant for SerializeExpressionStructVariant {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.map
-            .insert(ObjectKey::string(key), to_expression(&value)?);
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(ObjectKey::from(key), expr);
         Ok(())
     }
 
     fn end(self) -> Result<Self::Ok> {
         let mut object = Object::new();
-        object.insert(ObjectKey::String(self.name), Expression::Object(self.map));
+        object.insert(self.name, self.map.into());
         Ok(Expression::Object(object))
     }
 }
 
-struct ObjectKeySerializer;
+pub struct ObjectKeySerializer;
 
 impl ser::Serializer for ObjectKeySerializer {
     type Ok = ObjectKey;
@@ -353,7 +388,7 @@ impl ser::Serializer for ObjectKeySerializer {
     type SerializeStructVariant = Impossible<ObjectKey, Error>;
 
     serialize_unsupported! {
-        bool f32 f64 bytes unit unit_struct newtype_variant none
+        bool f32 f64 bytes unit unit_struct none
         some seq tuple tuple_struct tuple_variant map struct struct_variant
     }
 
@@ -413,6 +448,28 @@ impl ser::Serializer for ObjectKeySerializer {
         value.serialize(self)
     }
 
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        // Specialization for the `ObjectKey` type itself.
+        match (name, variant) {
+            ("$hcl::object_key", "Identifier") => Ok(ObjectKey::Identifier(
+                value.serialize(IdentifierSerializer)?,
+            )),
+            ("$hcl::object_key", "RawExpression") => Ok(ObjectKey::RawExpression(
+                value.serialize(StringSerializer)?.into(),
+            )),
+            (_, _) => value.serialize(self),
+        }
+    }
+
     fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
     where
         T: ?Sized + Display,
diff --git a/src/structure/ser/mod.rs b/src/structure/ser/mod.rs
new file mode 100644
index 0000000..8f9fe72
--- /dev/null
+++ b/src/structure/ser/mod.rs
@@ -0,0 +1,294 @@
+//! Serializer impls for HCL structure types.
+
+mod attribute;
+mod block;
+pub(crate) mod body;
+mod expression;
+mod structure;
+#[cfg(test)]
+mod tests;
+
+pub use self::expression::to_expression;
+use super::Identifier;
+use crate::{serialize_unsupported, Error, Result};
+use serde::ser::{self, Impossible, Serialize};
+use std::fmt::Display;
+
+pub struct IdentifierSerializer;
+
+impl ser::Serializer for IdentifierSerializer {
+    type Ok = Identifier;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<Identifier, Error>;
+    type SerializeTuple = Impossible<Identifier, Error>;
+    type SerializeTupleStruct = Impossible<Identifier, Error>;
+    type SerializeTupleVariant = Impossible<Identifier, Error>;
+    type SerializeMap = Impossible<Identifier, Error>;
+    type SerializeStruct = Impossible<Identifier, Error>;
+    type SerializeStructVariant = Impossible<Identifier, Error>;
+
+    serialize_unsupported! {
+        i8 i16 i32 i64 u8 u16 u32 u64
+        bool f32 f64 bytes unit unit_struct newtype_variant none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(Identifier(value.to_string()))
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(Identifier(value.to_owned()))
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(Identifier(variant.to_owned()))
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(Identifier(value.to_string()))
+    }
+}
+
+pub struct StringSerializer;
+
+impl ser::Serializer for StringSerializer {
+    type Ok = String;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<String, Error>;
+    type SerializeTuple = Impossible<String, Error>;
+    type SerializeTupleStruct = Impossible<String, Error>;
+    type SerializeTupleVariant = Impossible<String, Error>;
+    type SerializeMap = Impossible<String, Error>;
+    type SerializeStruct = Impossible<String, Error>;
+    type SerializeStructVariant = Impossible<String, Error>;
+
+    serialize_unsupported! {
+        bool f32 f64 bytes unit unit_struct newtype_variant none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(value.to_owned())
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(variant.to_owned())
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(value.to_string())
+    }
+}
+
+pub struct SeqSerializer<S> {
+    inner: S,
+}
+
+impl<S> SeqSerializer<S> {
+    pub fn new(inner: S) -> Self {
+        SeqSerializer { inner }
+    }
+}
+
+impl<S> ser::Serializer for SeqSerializer<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    type SerializeSeq = SerializeSeq<S>;
+    type SerializeTuple = SerializeSeq<S>;
+    type SerializeTupleStruct = SerializeSeq<S>;
+    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
+    type SerializeMap = Impossible<Self::Ok, Self::Error>;
+    type SerializeStruct = Impossible<Self::Ok, Self::Error>;
+    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit newtype_variant unit_struct unit_variant
+        tuple_variant map struct struct_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(
+        self,
+        _name: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok, Self::Error>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+        Ok(SerializeSeq::new(self.inner, len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+        self.serialize_seq(Some(len))
+    }
+}
+
+pub struct SerializeSeq<S>
+where
+    S: ser::Serializer,
+{
+    inner: S,
+    vec: Vec<S::Ok>,
+}
+
+impl<S> SerializeSeq<S>
+where
+    S: ser::Serializer,
+{
+    fn new(inner: S, len: Option<usize>) -> Self {
+        SerializeSeq {
+            inner,
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl<S> ser::SerializeSeq for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(self.inner.clone())?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        Ok(self.vec)
+    }
+}
+
+impl<S> ser::SerializeTuple for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl<S> serde::ser::SerializeTupleStruct for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        ser::SerializeSeq::end(self)
+    }
+}
diff --git a/src/structure/ser/structure.rs b/src/structure/ser/structure.rs
new file mode 100644
index 0000000..99d08b6
--- /dev/null
+++ b/src/structure/ser/structure.rs
@@ -0,0 +1,313 @@
+use super::{
+    attribute::{
+        AttributeSerializer, SerializeAttributeMap, SerializeAttributeSeq,
+        SerializeAttributeStruct, SerializeAttributeStructVariant, SerializeAttributeTupleVariant,
+    },
+    block::{BlockSerializer, SerializeBlockSeq, SerializeBlockStruct},
+};
+use crate::{serialize_unsupported, Error, Result, Structure};
+use serde::ser::{self, Serialize, SerializeMap};
+
+pub struct StructureSerializer;
+
+impl ser::Serializer for StructureSerializer {
+    type Ok = Structure;
+    type Error = Error;
+
+    type SerializeSeq = SerializeStructureSeq;
+    type SerializeTuple = SerializeStructureSeq;
+    type SerializeTupleStruct = SerializeStructureSeq;
+    type SerializeTupleVariant = SerializeStructureTupleVariant;
+    type SerializeMap = SerializeStructureMap;
+    type SerializeStruct = SerializeStructureStruct;
+    type SerializeStructVariant = SerializeStructureStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Structure>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Structure>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        // Specialization for the `Structure` type itself.
+        match (name, variant) {
+            ("$hcl::structure", "Block") => Ok(Structure::Block(value.serialize(BlockSerializer)?)),
+            ("$hcl::structure", "Attribute") => {
+                Ok(Structure::Attribute(value.serialize(AttributeSerializer)?))
+            }
+            (_, _) => AttributeSerializer
+                .serialize_newtype_variant(name, variant_index, variant, value)
+                .map(Into::into),
+        }
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeStructureSeq::new(len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeStructureTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeStructureMap::new())
+    }
+
+    fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeStructureStruct::new(name))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeStructureStructVariant::new(variant, len))
+    }
+}
+
+pub enum SerializeStructureSeq {
+    Attribute(SerializeAttributeSeq),
+    Block(SerializeBlockSeq),
+}
+
+impl SerializeStructureSeq {
+    fn new(len: Option<usize>) -> Self {
+        // Specialization for the `Block` type.
+        if let Some(3) = len {
+            SerializeStructureSeq::Block(SerializeBlockSeq::new())
+        } else {
+            SerializeStructureSeq::Attribute(SerializeAttributeSeq::new())
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureSeq::Attribute(attr) => attr.serialize_element(value),
+            SerializeStructureSeq::Block(block) => block.serialize_element(value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureSeq::Attribute(attr) => attr.end().map(Into::into),
+            SerializeStructureSeq::Block(block) => block.end().map(Into::into),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl ser::SerializeTupleStruct for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeStructureTupleVariant {
+    inner: SerializeAttributeTupleVariant,
+}
+
+impl SerializeStructureTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeStructureTupleVariant {
+            inner: SerializeAttributeTupleVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeStructureTupleVariant {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub struct SerializeStructureMap {
+    inner: SerializeAttributeMap,
+}
+
+impl SerializeStructureMap {
+    fn new() -> Self {
+        SerializeStructureMap {
+            inner: SerializeAttributeMap::new(),
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeStructureMap {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_key(key)
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_value(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub enum SerializeStructureStruct {
+    Attribute(SerializeAttributeStruct),
+    Block(SerializeBlockStruct),
+    Other(SerializeStructureMap),
+}
+
+impl SerializeStructureStruct {
+    fn new(name: &'static str) -> Self {
+        // Specialization for the `Attribute` and `Block` types.
+        match name {
+            "$hcl::attribute" => {
+                SerializeStructureStruct::Attribute(SerializeAttributeStruct::new())
+            }
+            "$hcl::block" => SerializeStructureStruct::Block(SerializeBlockStruct::new()),
+            _ => SerializeStructureStruct::Other(SerializeStructureMap::new()),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeStructureStruct {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureStruct::Attribute(ser) => ser.serialize_field(key, value),
+            SerializeStructureStruct::Block(ser) => ser.serialize_field(key, value),
+            SerializeStructureStruct::Other(ser) => ser.serialize_entry(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureStruct::Attribute(ser) => ser.end().map(Into::into),
+            SerializeStructureStruct::Block(ser) => ser.end().map(Into::into),
+            SerializeStructureStruct::Other(ser) => ser.end(),
+        }
+    }
+}
+
+pub struct SerializeStructureStructVariant {
+    inner: SerializeAttributeStructVariant,
+}
+
+impl SerializeStructureStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeStructureStructVariant {
+            inner: SerializeAttributeStructVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeStructureStructVariant {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(key, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}


Test patch:
diff --git a/src/ser/tests.rs b/src/ser/tests.rs
index dab8d6f..b4f4002 100644
--- a/src/ser/tests.rs
+++ b/src/ser/tests.rs
@@ -159,6 +159,8 @@ fn serialize_array() {
         },
         {
             "bar": "baz",
+        },
+        {
             "qux": {
                 "foo": "bar",
                 "baz": "qux"
diff --git a/src/structure/ser/tests.rs b/src/structure/ser/tests.rs
new file mode 100644
index 0000000..344ac74
--- /dev/null
+++ b/src/structure/ser/tests.rs
@@ -0,0 +1,151 @@
+use super::{
+    attribute::AttributeSerializer,
+    block::{BlockLabelSerializer, BlockSerializer},
+    body::BodySerializer,
+    expression::ExpressionSerializer,
+    *,
+};
+use crate::{Attribute, Block, BlockLabel, Body, Expression, Map};
+use serde::Serialize;
+use std::fmt::Debug;
+
+#[track_caller]
+fn test_identity<S, T>(ser: S, value: T)
+where
+    S: ser::Serializer<Ok = T>,
+    T: ser::Serialize + PartialEq + Debug,
+{
+    assert_eq!(value, value.serialize(ser).unwrap());
+}
+
+#[track_caller]
+fn test_serialize<S, G, E>(ser: S, given: G, expected: E)
+where
+    S: ser::Serializer<Ok = E>,
+    G: ser::Serialize,
+    E: PartialEq + Debug,
+{
+    assert_eq!(expected, given.serialize(ser).unwrap());
+}
+
+#[test]
+fn identity() {
+    test_identity(BodySerializer, Body::default());
+    test_identity(
+        BodySerializer,
+        Body::builder()
+            .add_attribute(("foo", "bar"))
+            .add_block(Block::builder("baz").build())
+            .build(),
+    );
+    test_identity(AttributeSerializer, Attribute::new("foo", "bar"));
+    test_identity(
+        AttributeSerializer,
+        Attribute::new("foo", vec!["bar", "baz"]),
+    );
+    test_identity(
+        BlockSerializer,
+        Block::builder("foo")
+            .add_label("bar")
+            .add_attribute(("baz", "qux"))
+            .build(),
+    );
+    test_identity(BlockLabelSerializer, BlockLabel::string("foo"));
+    test_identity(BlockLabelSerializer, BlockLabel::identifier("foo"));
+    test_identity(ExpressionSerializer, Expression::String("bar".into()));
+    test_identity(
+        ExpressionSerializer,
+        Expression::from_iter([("foo", "bar")]),
+    );
+}
+
+#[test]
+fn custom() {
+    #[derive(Serialize)]
+    struct CustomAttr {
+        key: &'static str,
+        #[serde(rename = "expr")]
+        value: &'static str,
+    }
+    test_serialize(
+        AttributeSerializer,
+        CustomAttr {
+            key: "foo",
+            value: "bar",
+        },
+        Attribute::new("foo", "bar"),
+    );
+    test_serialize(
+        AttributeSerializer,
+        ("foo", "bar"),
+        Attribute::new("foo", "bar"),
+    );
+
+    test_serialize(
+        BlockSerializer,
+        {
+            let mut map = Map::new();
+            map.insert("foo", (("bar", "baz"), ("qux", "foo")));
+            map
+        },
+        Block::builder("foo")
+            .add_attribute(("bar", "baz"))
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    #[derive(Serialize)]
+    struct CustomBlock {
+        #[serde(rename = "identifier")]
+        ident: &'static str,
+        body: Map<&'static str, &'static str>,
+    }
+
+    test_serialize(
+        BlockSerializer,
+        CustomBlock {
+            ident: "foo",
+            body: {
+                let mut map = Map::new();
+                map.insert("bar", "baz");
+                map.insert("qux", "foo");
+                map
+            },
+        },
+        Block::builder("foo")
+            .add_attribute(("bar", "baz"))
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    #[derive(Serialize)]
+    struct CustomLabeledBlock {
+        #[serde(rename = "identifier")]
+        ident: &'static str,
+        labels: [&'static str; 2],
+        body: Map<&'static str, &'static str>,
+    }
+
+    test_serialize(
+        BlockSerializer,
+        CustomLabeledBlock {
+            ident: "foo",
+            labels: ["bar", "baz"],
+            body: {
+                let mut map = Map::new();
+                map.insert("qux", "foo");
+                map
+            },
+        },
+        Block::builder("foo")
+            .add_labels(["bar", "baz"])
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    test_serialize(
+        ExpressionSerializer,
+        Some(1u8),
+        Expression::Number(1u8.into()),
+    );
+}