Repository: martinohmann/hcl-rs
Pull request #135 (2022-11-25 19:29:35)

feat(serde)!: use internal serialization to roundtrip crate types
BREAKING CHANGE: `hcl::ser::Serializer` does not implement `serde::Serializer` anymore. Use `serializer.serialize(&value)` in places where `value.serialize(&mut serializer)` was used before.

This essentially uses the approach that the author of `minijinja` describes in this blog post: https://lucumr.pocoo.org/2021/11/14/abusing-serde/

I originally read this before I even added serialization support to `hcl-rs` and decided back then to go without internal serialization.

However, the number of types involved to represent the HCL structural, expression and template languages now makes it necessary to switch to internal serialization since each type needs a custom serializer implementation just for the sake of roundtripping it through `serde`. The amount of code that I wrote to make this work is already quite massive and would even get bigger once serialization of template via `serde` is fully supported. Also it was quite messy.

Thus, I switched to internal serialization to roundtrip all expression and structure types. This meant that I could remove a lot of custom serializers.

This also had some positive side effects:

- The `#[serde(rename = "$hcl::...")]` markers could be removed almost everywhere since they were only needed to make the roundtripping through serde work. The only type that still has this marker is `Body` since we need to special-case it during deserialization.
- The serializer code is much cleaner now.
- Serialization of types that are subject to roundtripping is quite a bit faster.
- It is now possible (and this change already implements most of it) to properly support serialization of HCL blocks (with or without labels) from custom types. The public API and documentation for this will be added as a followup. The `custom_blocks` test in `src/structure/ser/tests.rs` should give a first hint how this will work.


Patch:
diff --git a/src/de/mod.rs b/src/de/mod.rs
index ceb94ce..b2f6459 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -9,6 +9,7 @@
 mod tests;
 
 use crate::{parser, Body, Error, Identifier, Result, Value};
+use serde::de::value::StringDeserializer;
 use serde::de::{self, Deserializer as _, IntoDeserializer};
 use serde::forward_to_deserialize_any;
 use std::fmt;
@@ -229,7 +230,7 @@ impl<'de> de::Deserializer<'de> for Deserializer {
     where
         V: de::Visitor<'de>,
     {
-        if name == "$hcl::body" {
+        if name == "$hcl::Body" {
             // Specialized handling of `hcl::Body`.
             self.body.into_deserializer().deserialize_any(visitor)
         } else {
@@ -459,9 +460,9 @@ where
 }
 
 impl<'de> IntoDeserializer<'de, Error> for Identifier {
-    type Deserializer = NewtypeStructDeserializer<String>;
+    type Deserializer = StringDeserializer<Error>;
 
     fn into_deserializer(self) -> Self::Deserializer {
-        NewtypeStructDeserializer::new(self.into_inner())
+        self.into_inner().into_deserializer()
     }
 }
diff --git a/src/expr/conditional.rs b/src/expr/conditional.rs
index 1acd829..aa024c6 100644
--- a/src/expr/conditional.rs
+++ b/src/expr/conditional.rs
@@ -1,10 +1,9 @@
 use super::Expression;
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 
 /// The conditional operator allows selecting from one of two expressions based on the outcome of a
 /// boolean expression.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::conditional")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct Conditional {
     /// A condition expression that evaluates to a boolean value.
     pub cond_expr: Expression,
diff --git a/src/expr/de.rs b/src/expr/de.rs
index 72f0c04..4e8f1db 100644
--- a/src/expr/de.rs
+++ b/src/expr/de.rs
@@ -1,12 +1,33 @@
 //! Deserialize impls for HCL structure types.
 
 use super::*;
-use crate::de::{NewtypeStructDeserializer, OptionDeserializer, VariantName};
+use crate::de::{FromStrVisitor, OptionDeserializer, VariantName};
 use crate::{Error, Identifier, Result};
-use serde::de::value::{MapAccessDeserializer, StrDeserializer};
+use serde::de::value::{MapAccessDeserializer, StrDeserializer, StringDeserializer};
 use serde::de::{self, IntoDeserializer};
 use serde::{forward_to_deserialize_any, Deserializer};
 
+macro_rules! impl_deserialize_for_operator {
+    ($($ty:ty => $expr:expr),*) => {
+        $(
+            impl<'de> de::Deserialize<'de> for $ty {
+                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+                where
+                    D: de::Deserializer<'de>,
+                {
+                    deserializer.deserialize_any(FromStrVisitor::<Self>::new($expr))
+                }
+            }
+        )*
+    };
+}
+
+impl_deserialize_for_operator! {
+    UnaryOperator => "a unary operator",
+    BinaryOperator => "a binary operator",
+    HeredocStripMode => "a heredoc strip mode"
+}
+
 impl<'de> IntoDeserializer<'de, Error> for Expression {
     type Deserializer = Self;
 
@@ -763,10 +784,10 @@ impl<'de> de::VariantAccess<'de> for ObjectKey {
 }
 
 impl<'de> IntoDeserializer<'de, Error> for RawExpression {
-    type Deserializer = NewtypeStructDeserializer<String>;
+    type Deserializer = StringDeserializer<Error>;
 
     fn into_deserializer(self) -> Self::Deserializer {
-        NewtypeStructDeserializer::new(self.into_inner())
+        self.into_inner().into_deserializer()
     }
 }
 
@@ -875,7 +896,7 @@ impl<'de> IntoDeserializer<'de, Error> for HeredocStripMode {
 }
 
 impl<'de> IntoDeserializer<'de, Error> for Variable {
-    type Deserializer = NewtypeStructDeserializer<String>;
+    type Deserializer = StringDeserializer<Error>;
 
     fn into_deserializer(self) -> Self::Deserializer {
         self.into_inner().into_deserializer()
diff --git a/src/expr/for_expr.rs b/src/expr/for_expr.rs
index fda3763..af46915 100644
--- a/src/expr/for_expr.rs
+++ b/src/expr/for_expr.rs
@@ -1,11 +1,10 @@
 use super::Expression;
 use crate::Identifier;
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 
 /// A for expression is a construct for constructing a collection by projecting the items from
 /// another collection.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::for_expr")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct ForExpr {
     /// Optional name of the variable that will be temporarily assigned the key of each element
     /// during iteration. If the source collection is an array, it gets assigned the zero-based
diff --git a/src/expr/func_call.rs b/src/expr/func_call.rs
index 212157e..345fc48 100644
--- a/src/expr/func_call.rs
+++ b/src/expr/func_call.rs
@@ -1,10 +1,9 @@
 use super::Expression;
 use crate::Identifier;
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 
 /// Represents a function call expression with zero or more arguments.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::func_call")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
 pub struct FuncCall {
     /// The name of the function.
     pub name: Identifier,
diff --git a/src/expr/mod.rs b/src/expr/mod.rs
index c3de91e..b6ab2ff 100644
--- a/src/expr/mod.rs
+++ b/src/expr/mod.rs
@@ -25,7 +25,9 @@ pub use self::{
     traversal::{Traversal, TraversalBuilder, TraversalOperator},
     variable::Variable,
 };
-use crate::{format, Identifier, Number, Result, Value};
+use crate::format;
+use crate::ser::with_internal_serialization;
+use crate::{Identifier, Number, Result, Value};
 use serde::{Deserialize, Serialize};
 use std::borrow::Cow;
 use std::fmt::{self, Display, Write};
@@ -35,8 +37,7 @@ pub type Object<K, V> = vecmap::VecMap<K, V>;
 
 /// A type representing the expression sub-language. It is used in HCL attributes to specify
 /// values and in HCL templates.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::expression")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
 #[non_exhaustive]
 pub enum Expression {
     /// Represents a null value.
@@ -74,6 +75,16 @@ pub enum Expression {
     Raw(RawExpression),
 }
 
+impl Expression {
+    #[doc(hidden)]
+    pub fn from_serializable<T>(value: &T) -> Result<Expression>
+    where
+        T: ?Sized + Serialize,
+    {
+        with_internal_serialization(|| value.serialize(ExpressionSerializer))
+    }
+}
+
 impl From<Expression> for Value {
     fn from(expr: Expression) -> Self {
         match expr {
@@ -231,6 +242,18 @@ impl From<Operation> for Expression {
     }
 }
 
+impl From<UnaryOp> for Expression {
+    fn from(op: UnaryOp) -> Self {
+        Expression::from(Operation::Unary(op))
+    }
+}
+
+impl From<BinaryOp> for Expression {
+    fn from(op: BinaryOp) -> Self {
+        Expression::from(Operation::Binary(op))
+    }
+}
+
 impl From<ForExpr> for Expression {
     fn from(expr: ForExpr) -> Self {
         Expression::ForExpr(Box::new(expr))
@@ -243,6 +266,12 @@ impl From<TemplateExpr> for Expression {
     }
 }
 
+impl From<Heredoc> for Expression {
+    fn from(heredoc: Heredoc) -> Self {
+        Expression::from(TemplateExpr::Heredoc(heredoc))
+    }
+}
+
 impl From<Variable> for Expression {
     fn from(variable: Variable) -> Self {
         Expression::Variable(variable)
@@ -260,7 +289,6 @@ impl Display for Expression {
 
 /// Represents an object key.
 #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::object_key")]
 #[non_exhaustive]
 pub enum ObjectKey {
     /// Represents an unquoted identifier used as object key.
@@ -327,8 +355,8 @@ impl Display for ObjectKey {
 ///
 /// *Please note*: raw expressions are not validated during serialization, so it is your
 /// responsiblity to ensure that they are valid HCL.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)]
-#[serde(rename = "$hcl::raw_expression")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
+#[serde(transparent)]
 pub struct RawExpression(String);
 
 impl RawExpression {
@@ -396,5 +424,5 @@ pub fn to_expression<T>(value: T) -> Result<Expression>
 where
     T: Serialize,
 {
-    value.serialize(ExpressionSerializer)
+    Expression::from_serializable(&value)
 }
diff --git a/src/expr/operation.rs b/src/expr/operation.rs
index 5c118eb..5e929c8 100644
--- a/src/expr/operation.rs
+++ b/src/expr/operation.rs
@@ -1,13 +1,11 @@
 use super::Expression;
-use crate::de::FromStrVisitor;
 use crate::{Error, Result};
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 use std::fmt;
 use std::str::FromStr;
 
 /// Operations apply a particular operator to either one or two expression terms.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::operation")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub enum Operation {
     /// Represents an operation that applies an operator to a single expression.
     Unary(UnaryOp),
@@ -28,8 +26,7 @@ impl From<BinaryOp> for Operation {
 }
 
 /// An operation that applies an operator to one expression.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::unary_op")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct UnaryOp {
     /// The unary operator to use on the expression.
     pub operator: UnaryOperator,
@@ -87,27 +84,8 @@ impl FromStr for UnaryOperator {
     }
 }
 
-impl Serialize for UnaryOperator {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        serializer.serialize_str(self.as_str())
-    }
-}
-
-impl<'de> Deserialize<'de> for UnaryOperator {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(FromStrVisitor::<Self>::new("a unary operator"))
-    }
-}
-
 /// An operation that applies an operator to two expressions.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::binary_op")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct BinaryOp {
     /// The expression on the left-hand-side of the operation.
     pub lhs_expr: Expression,
@@ -322,21 +300,3 @@ impl FromStr for BinaryOperator {
         }
     }
 }
-
-impl Serialize for BinaryOperator {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        serializer.serialize_str(self.as_str())
-    }
-}
-
-impl<'de> Deserialize<'de> for BinaryOperator {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(FromStrVisitor::<Self>::new("a binary operator"))
-    }
-}
diff --git a/src/expr/ser/conditional.rs b/src/expr/ser/conditional.rs
deleted file mode 100644
index eb119ba..0000000
--- a/src/expr/ser/conditional.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use super::ExpressionSerializer;
-use crate::expr::{Conditional, Expression};
-use crate::{Error, Result};
-use serde::ser::{self, Impossible};
-
-pub struct ConditionalSerializer;
-
-impl ser::Serializer for ConditionalSerializer {
-    type Ok = Conditional;
-    type Error = Error;
-
-    type SerializeSeq = SerializeConditionalSeq;
-    type SerializeTuple = SerializeConditionalSeq;
-    type SerializeTupleStruct = SerializeConditionalSeq;
-    type SerializeTupleVariant = Impossible<Conditional, Error>;
-    type SerializeMap = Impossible<Conditional, Error>;
-    type SerializeStruct = SerializeConditionalStruct;
-    type SerializeStructVariant = Impossible<Conditional, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        newtype_variant tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeConditionalSeq::new())
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeConditionalStruct::new())
-    }
-}
-
-pub struct SerializeConditionalSeq {
-    cond_expr: Option<Expression>,
-    true_expr: Option<Expression>,
-    false_expr: Option<Expression>,
-}
-
-impl SerializeConditionalSeq {
-    pub fn new() -> Self {
-        SerializeConditionalSeq {
-            cond_expr: None,
-            true_expr: None,
-            false_expr: None,
-        }
-    }
-}
-
-impl ser::SerializeSeq for SerializeConditionalSeq {
-    type Ok = Conditional;
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if self.cond_expr.is_none() {
-            self.cond_expr = Some(value.serialize(ExpressionSerializer)?);
-        } else if self.true_expr.is_none() {
-            self.true_expr = Some(value.serialize(ExpressionSerializer)?);
-        } else if self.false_expr.is_none() {
-            self.false_expr = Some(value.serialize(ExpressionSerializer)?);
-        } else {
-            return Err(ser::Error::custom("expected sequence with 3 elements"));
-        }
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.cond_expr, self.true_expr, self.false_expr) {
-            (Some(cond_expr), Some(true_expr), Some(false_expr)) => Ok(Conditional {
-                cond_expr,
-                true_expr,
-                false_expr,
-            }),
-            (_, _, _) => Err(ser::Error::custom("expected sequence with 3 elements")),
-        }
-    }
-}
-
-impl ser::SerializeTuple for SerializeConditionalSeq {
-    impl_forward_to_serialize_seq!(serialize_element, Conditional);
-}
-
-impl ser::SerializeTupleStruct for SerializeConditionalSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Conditional);
-}
-
-pub struct SerializeConditionalStruct {
-    cond_expr: Option<Expression>,
-    true_expr: Option<Expression>,
-    false_expr: Option<Expression>,
-}
-
-impl SerializeConditionalStruct {
-    pub fn new() -> Self {
-        SerializeConditionalStruct {
-            cond_expr: None,
-            true_expr: None,
-            false_expr: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeConditionalStruct {
-    type Ok = Conditional;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "cond_expr" => self.cond_expr = Some(value.serialize(ExpressionSerializer)?),
-            "true_expr" => self.true_expr = Some(value.serialize(ExpressionSerializer)?),
-            "false_expr" => self.false_expr = Some(value.serialize(ExpressionSerializer)?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `cond_expr`, `true_expr` and `false_expr`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.cond_expr, self.true_expr, self.false_expr) {
-            (Some(cond_expr), Some(true_expr), Some(false_expr)) => Ok(Conditional {
-                cond_expr,
-                true_expr,
-                false_expr,
-            }),
-            (_, _, _) => Err(ser::Error::custom(
-                "expected struct with fields `cond_expr`, `true_expr` and `false_expr`",
-            )),
-        }
-    }
-}
diff --git a/src/expr/ser/for_expr.rs b/src/expr/ser/for_expr.rs
deleted file mode 100644
index 93df814..0000000
--- a/src/expr/ser/for_expr.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-use super::ExpressionSerializer;
-use crate::expr::{Expression, ForExpr};
-use crate::ser::{BoolSerializer, IdentifierSerializer, OptionSerializer};
-use crate::{Error, Identifier, Result};
-use serde::ser::{self, Impossible};
-
-pub struct ForExprSerializer;
-
-impl ser::Serializer for ForExprSerializer {
-    type Ok = ForExpr;
-    type Error = Error;
-
-    type SerializeSeq = Impossible<ForExpr, Error>;
-    type SerializeTuple = Impossible<ForExpr, Error>;
-    type SerializeTupleStruct = Impossible<ForExpr, Error>;
-    type SerializeTupleVariant = Impossible<ForExpr, Error>;
-    type SerializeMap = Impossible<ForExpr, Error>;
-    type SerializeStruct = SerializeForExprStruct;
-    type SerializeStructVariant = Impossible<ForExpr, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str
-        bytes none unit unit_struct unit_variant newtype_variant
-        seq tuple tuple_struct tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeForExprStruct::new())
-    }
-}
-
-pub struct SerializeForExprStruct {
-    key_var: Option<Option<Identifier>>,
-    value_var: Option<Identifier>,
-    collection_expr: Option<Expression>,
-    key_expr: Option<Option<Expression>>,
-    value_expr: Option<Expression>,
-    grouping: Option<bool>,
-    cond_expr: Option<Option<Expression>>,
-}
-
-impl SerializeForExprStruct {
-    pub fn new() -> Self {
-        SerializeForExprStruct {
-            key_var: None,
-            value_var: None,
-            collection_expr: None,
-            key_expr: None,
-            value_expr: None,
-            grouping: None,
-            cond_expr: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeForExprStruct {
-    type Ok = ForExpr;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "key_var" => {
-                self.key_var = Some(value.serialize(OptionSerializer::new(IdentifierSerializer))?);
-            }
-            "value_var" => {
-                self.value_var = Some(value.serialize(IdentifierSerializer)?);
-            }
-            "collection_expr" => {
-                self.collection_expr = Some(value.serialize(ExpressionSerializer)?);
-            }
-            "key_expr" => self.key_expr = Some(value.serialize(OptionSerializer::new(ExpressionSerializer))?),
-            "value_expr" => self.value_expr = Some(value.serialize(ExpressionSerializer)?),
-            "grouping" => self.grouping = Some(value.serialize(BoolSerializer)?),
-            "cond_expr" => self.cond_expr = Some(value.serialize(OptionSerializer::new(ExpressionSerializer))?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `key_var`, `value_var`, `collection_expr`, `key_expr`, `value_expr`, `grouping` and `cond_expr`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (
-            self.key_var,
-            self.value_var,
-            self.collection_expr,
-            self.key_expr,
-            self.value_expr,
-            self.grouping,
-            self.cond_expr
-        ) {
-            (
-                Some(key_var),
-                Some(value_var),
-                Some(collection_expr),
-                Some(key_expr),
-                Some(value_expr),
-                Some(grouping),
-                Some(cond_expr)
-            ) => Ok(ForExpr {
-                key_var,
-                value_var,
-                collection_expr,
-                key_expr,
-                value_expr,
-                grouping,
-                cond_expr,
-            }),
-            (_, _, _, _, _, _, _) => Err(ser::Error::custom(
-                "expected struct with fields `key_var`, `value_var`, `collection_expr`, `key_expr`, `value_expr`, `grouping` and `cond_expr`",
-            )),
-        }
-    }
-}
diff --git a/src/expr/ser/func_call.rs b/src/expr/ser/func_call.rs
deleted file mode 100644
index bb75d47..0000000
--- a/src/expr/ser/func_call.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use super::ExpressionSerializer;
-use crate::expr::{Expression, FuncCall};
-use crate::ser::{BoolSerializer, IdentifierSerializer, SeqSerializer};
-use crate::{Error, Identifier, Result};
-use serde::ser::{self, Impossible};
-
-pub struct FuncCallSerializer;
-
-impl ser::Serializer for FuncCallSerializer {
-    type Ok = FuncCall;
-    type Error = Error;
-
-    type SerializeSeq = Impossible<FuncCall, Error>;
-    type SerializeTuple = Impossible<FuncCall, Error>;
-    type SerializeTupleStruct = Impossible<FuncCall, Error>;
-    type SerializeTupleVariant = Impossible<FuncCall, Error>;
-    type SerializeMap = Impossible<FuncCall, Error>;
-    type SerializeStruct = SerializeFuncCallStruct;
-    type SerializeStructVariant = Impossible<FuncCall, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        newtype_variant seq tuple tuple_struct tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeFuncCallStruct::new())
-    }
-}
-
-pub struct SerializeFuncCallStruct {
-    name: Option<Identifier>,
-    args: Option<Vec<Expression>>,
-    expand_final: Option<bool>,
-}
-
-impl SerializeFuncCallStruct {
-    pub fn new() -> Self {
-        SerializeFuncCallStruct {
-            name: None,
-            args: None,
-            expand_final: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeFuncCallStruct {
-    type Ok = FuncCall;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "name" => self.name = Some(value.serialize(IdentifierSerializer)?),
-            "args" => self.args = Some(value.serialize(SeqSerializer::new(ExpressionSerializer))?),
-            "expand_final" => self.expand_final = Some(value.serialize(BoolSerializer)?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `name`, `args` and optional `expand_final`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.name, self.args) {
-            (Some(name), Some(args)) => Ok(FuncCall {
-                name,
-                args,
-                expand_final: self.expand_final.unwrap_or_default(),
-            }),
-            (_, _) => Err(ser::Error::custom(
-                "expected struct with fields `name`, `args` and optional `expand_final`",
-            )),
-        }
-    }
-}
diff --git a/src/expr/ser/mod.rs b/src/expr/ser/mod.rs
index 30a3c36..f2cbc29 100644
--- a/src/expr/ser/mod.rs
+++ b/src/expr/ser/mod.rs
@@ -1,30 +1,93 @@
 //! Serializer impls for HCL expression types.
 
-mod conditional;
-mod for_expr;
-mod func_call;
-mod operation;
-mod template_expr;
 #[cfg(test)]
 mod tests;
-mod traversal;
-
-use self::{
-    conditional::{ConditionalSerializer, SerializeConditionalStruct},
-    for_expr::{ForExprSerializer, SerializeForExprStruct},
-    func_call::{FuncCallSerializer, SerializeFuncCallStruct},
-    operation::{OperationSerializer, SerializeOperationStruct},
-    template_expr::{SerializeTemplateExprStruct, TemplateExprSerializer},
-    traversal::{SerializeTraversalStruct, TraversalSerializer},
-};
-use crate::expr::{Expression, Object, ObjectKey, RawExpression, Variable};
-use crate::ser::{IdentifierSerializer, StringSerializer};
-use crate::{Error, Identifier, Number, Result};
+
+use super::*;
+use crate::ser::{in_internal_serialization, InternalHandles, SerializeInternalHandleStruct};
+use crate::{format, Error, Identifier, Number, Result};
 use serde::ser::{self, Impossible, SerializeMap};
-use std::fmt::Display;
+use std::fmt;
+
+const EXPR_HANDLE_MARKER: &str = "\x00$hcl::ExprHandle";
+
+thread_local! {
+    static EXPR_HANDLES: InternalHandles<Expression> = InternalHandles::new(EXPR_HANDLE_MARKER);
+}
+
+macro_rules! impl_serialize_for_expr {
+    ($($ty:ty)*) => {
+        $(
+            impl ser::Serialize for $ty {
+                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where
+                    S: ser::Serializer,
+                {
+                    if in_internal_serialization() {
+                        EXPR_HANDLES.with(|eh| eh.serialize(self.clone(), serializer))
+                    } else {
+                        let s = format::to_interpolated_string(self).map_err(ser::Error::custom)?;
+                        serializer.serialize_str(&s)
+                    }
+                }
+            }
+        )*
+    };
+}
+
+macro_rules! impl_serialize_for_operator {
+    ($($ty:ty)*) => {
+        $(
+            impl ser::Serialize for $ty {
+                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where
+                    S: ser::Serializer,
+                {
+                    serializer.serialize_str(self.as_str())
+                }
+            }
+        )*
+    };
+}
+
+impl_serialize_for_expr! {
+    Conditional ForExpr FuncCall Operation UnaryOp BinaryOp
+    TemplateExpr Heredoc RawExpression Traversal Variable
+}
+impl_serialize_for_operator! {
+    UnaryOperator BinaryOperator HeredocStripMode
+}
+
+impl ser::Serialize for Expression {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        if in_internal_serialization() {
+            return EXPR_HANDLES.with(|eh| eh.serialize(self.clone(), serializer));
+        }
+
+        match self {
+            Expression::Null => serializer.serialize_unit(),
+            Expression::Bool(b) => serializer.serialize_bool(*b),
+            Expression::Number(num) => num.serialize(serializer),
+            Expression::String(s) => serializer.serialize_str(s),
+            Expression::Array(array) => array.serialize(serializer),
+            Expression::Object(object) => object.serialize(serializer),
+            Expression::Parenthesis(expr) => expr.serialize(serializer),
+            Expression::TemplateExpr(expr) => expr.serialize(serializer),
+            Expression::FuncCall(func_call) => func_call.serialize(serializer),
+            Expression::Variable(var) => var.serialize(serializer),
+            Expression::Traversal(traversal) => traversal.serialize(serializer),
+            Expression::Conditional(cond) => cond.serialize(serializer),
+            Expression::Operation(op) => op.serialize(serializer),
+            Expression::ForExpr(expr) => expr.serialize(serializer),
+            Expression::Raw(raw) => raw.serialize(serializer),
+        }
+    }
+}
 
-#[derive(Clone)]
-pub struct ExpressionSerializer;
+pub(crate) struct ExpressionSerializer;
 
 impl ser::Serializer for ExpressionSerializer {
     type Ok = Expression;
@@ -38,7 +101,7 @@ impl ser::Serializer for ExpressionSerializer {
     type SerializeStruct = SerializeExpressionStruct;
     type SerializeStructVariant = SerializeExpressionStructVariant;
 
-    serialize_self! { some }
+    serialize_self! { some newtype_struct }
     forward_to_serialize_seq! { tuple tuple_struct }
 
     fn serialize_bool(self, value: bool) -> Result<Self::Ok> {
@@ -111,37 +174,16 @@ impl ser::Serializer for ExpressionSerializer {
 
     fn serialize_unit_variant(
         self,
-        name: &'static str,
+        _name: &'static str,
         _variant_index: u32,
         variant: &'static str,
     ) -> Result<Self::Ok> {
-        if name == "$hcl::expression" && variant == "Null" {
-            Ok(Expression::Null)
-        } else {
-            self.serialize_str(variant)
-        }
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if name == "$hcl::raw_expression" {
-            Ok(Expression::Raw(RawExpression::from(
-                value.serialize(StringSerializer)?,
-            )))
-        } else if name == "$hcl::identifier" {
-            Ok(Expression::Variable(Variable::from(
-                value.serialize(IdentifierSerializer)?,
-            )))
-        } else {
-            value.serialize(self)
-        }
+        self.serialize_str(variant)
     }
 
     fn serialize_newtype_variant<T>(
         self,
-        name: &'static str,
+        _name: &'static str,
         _variant_index: u32,
         variant: &'static str,
         value: &T,
@@ -149,41 +191,12 @@ impl ser::Serializer for ExpressionSerializer {
     where
         T: ?Sized + ser::Serialize,
     {
-        match (name, variant) {
-            ("$hcl::expression", "Conditional") => {
-                Ok(Expression::from(value.serialize(ConditionalSerializer)?))
-            }
-            ("$hcl::expression", "Operation") | ("$hcl::operation", _) => {
-                Ok(Expression::from(value.serialize(OperationSerializer)?))
-            }
-            ("$hcl::expression", "ForExpr") => {
-                Ok(Expression::from(value.serialize(ForExprSerializer)?))
-            }
-            ("$hcl::expression", "FuncCall") => {
-                Ok(Expression::from(value.serialize(FuncCallSerializer)?))
-            }
-            ("$hcl::expression", "Parenthesis") => {
-                Ok(Expression::Parenthesis(Box::new(value.serialize(self)?)))
-            }
-            ("$hcl::expression", "TemplateExpr") | ("$hcl::template_expr", _) => {
-                Ok(Expression::from(value.serialize(TemplateExprSerializer)?))
-            }
-            ("$hcl::expression", "Traversal") => {
-                Ok(Expression::from(value.serialize(TraversalSerializer)?))
-            }
-            ("$hcl::expression", "Variable") => Ok(Expression::from(Variable::from(
-                value.serialize(IdentifierSerializer)?,
-            ))),
-            ("$hcl::expression", _) => value.serialize(self),
-            (_, _) => {
-                let mut object = Object::with_capacity(1);
-                object.insert(
-                    ObjectKey::Identifier(Identifier::new(variant)?),
-                    value.serialize(self)?,
-                );
-                Ok(Expression::Object(object))
-            }
-        }
+        let mut object = Object::with_capacity(1);
+        object.insert(
+            ObjectKey::Identifier(Identifier::new(variant)?),
+            value.serialize(self)?,
+        );
+        Ok(Expression::Object(object))
     }
 
     fn serialize_none(self) -> Result<Self::Ok> {
@@ -224,18 +237,18 @@ impl ser::Serializer for ExpressionSerializer {
 
     fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
     where
-        T: ?Sized + Display,
+        T: ?Sized + fmt::Display,
     {
         Ok(Expression::String(value.to_string()))
     }
 }
 
-pub struct SerializeExpressionSeq {
+pub(crate) struct SerializeExpressionSeq {
     vec: Vec<Expression>,
 }
 
 impl SerializeExpressionSeq {
-    pub fn new(len: Option<usize>) -> Self {
+    fn new(len: Option<usize>) -> Self {
         SerializeExpressionSeq {
             vec: Vec::with_capacity(len.unwrap_or(0)),
         }
@@ -267,13 +280,13 @@ impl ser::SerializeTupleStruct for SerializeExpressionSeq {
     impl_forward_to_serialize_seq!(serialize_field, Expression);
 }
 
-pub struct SerializeExpressionTupleVariant {
+pub(crate) struct SerializeExpressionTupleVariant {
     name: ObjectKey,
     vec: Vec<Expression>,
 }
 
 impl SerializeExpressionTupleVariant {
-    pub fn new(variant: &'static str, len: usize) -> Self {
+    pub(crate) fn new(variant: &'static str, len: usize) -> Self {
         SerializeExpressionTupleVariant {
             name: ObjectKey::from(variant),
             vec: Vec::with_capacity(len),
@@ -300,13 +313,13 @@ impl ser::SerializeTupleVariant for SerializeExpressionTupleVariant {
     }
 }
 
-pub struct SerializeExpressionMap {
+pub(crate) struct SerializeExpressionMap {
     map: Object<ObjectKey, Expression>,
     next_key: Option<ObjectKey>,
 }
 
 impl SerializeExpressionMap {
-    pub fn new(len: Option<usize>) -> Self {
+    pub(crate) fn new(len: Option<usize>) -> Self {
         SerializeExpressionMap {
             map: Object::with_capacity(len.unwrap_or(0)),
             next_key: None,
@@ -342,36 +355,17 @@ impl ser::SerializeMap for SerializeExpressionMap {
     }
 }
 
-pub enum SerializeExpressionStruct {
-    Conditional(SerializeConditionalStruct),
-    Traversal(SerializeTraversalStruct),
-    ForExpr(SerializeForExprStruct),
-    FuncCall(SerializeFuncCallStruct),
-    Operation(SerializeOperationStruct),
-    TemplateExpr(SerializeTemplateExprStruct),
-    Other(SerializeExpressionMap),
+pub(crate) enum SerializeExpressionStruct {
+    InternalHandle(SerializeInternalHandleStruct),
+    Map(SerializeExpressionMap),
 }
 
 impl SerializeExpressionStruct {
-    fn new(name: &'static str, len: usize) -> Self {
-        match name {
-            "$hcl::conditional" => {
-                SerializeExpressionStruct::Conditional(SerializeConditionalStruct::new())
-            }
-            "$hcl::traversal" => {
-                SerializeExpressionStruct::Traversal(SerializeTraversalStruct::new())
-            }
-            "$hcl::for_expr" => SerializeExpressionStruct::ForExpr(SerializeForExprStruct::new()),
-            "$hcl::func_call" => {
-                SerializeExpressionStruct::FuncCall(SerializeFuncCallStruct::new())
-            }
-            "$hcl::unary_op" | "$hcl::binary_op" => {
-                SerializeExpressionStruct::Operation(SerializeOperationStruct::new(name))
-            }
-            "$hcl::heredoc" => {
-                SerializeExpressionStruct::TemplateExpr(SerializeTemplateExprStruct::new())
-            }
-            _ => SerializeExpressionStruct::Other(SerializeExpressionMap::new(Some(len))),
+    pub(crate) fn new(name: &'static str, len: usize) -> Self {
+        if name == EXPR_HANDLE_MARKER {
+            SerializeExpressionStruct::InternalHandle(SerializeInternalHandleStruct::new())
+        } else {
+            SerializeExpressionStruct::Map(SerializeExpressionMap::new(Some(len)))
         }
     }
 }
@@ -385,36 +379,28 @@ impl ser::SerializeStruct for SerializeExpressionStruct {
         T: ?Sized + ser::Serialize,
     {
         match self {
-            SerializeExpressionStruct::Conditional(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::Traversal(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::ForExpr(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::FuncCall(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::Operation(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::TemplateExpr(ser) => ser.serialize_field(key, value),
-            SerializeExpressionStruct::Other(ser) => ser.serialize_entry(key, value),
+            SerializeExpressionStruct::InternalHandle(ser) => ser.serialize_field(key, value),
+            SerializeExpressionStruct::Map(ser) => ser.serialize_entry(key, value),
         }
     }
 
     fn end(self) -> Result<Self::Ok> {
         match self {
-            SerializeExpressionStruct::Conditional(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::Traversal(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::ForExpr(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::FuncCall(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::Operation(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::TemplateExpr(ser) => ser.end().map(Into::into),
-            SerializeExpressionStruct::Other(ser) => ser.end(),
+            SerializeExpressionStruct::InternalHandle(ser) => ser
+                .end()
+                .map(|handle| EXPR_HANDLES.with(|eh| eh.remove(handle))),
+            SerializeExpressionStruct::Map(ser) => ser.end(),
         }
     }
 }
 
-pub struct SerializeExpressionStructVariant {
+pub(crate) struct SerializeExpressionStructVariant {
     name: ObjectKey,
     map: Object<ObjectKey, Expression>,
 }
 
 impl SerializeExpressionStructVariant {
-    pub fn new(variant: &'static str, len: usize) -> Self {
+    pub(crate) fn new(variant: &'static str, len: usize) -> Self {
         SerializeExpressionStructVariant {
             name: ObjectKey::from(variant),
             map: Object::with_capacity(len),
@@ -442,7 +428,7 @@ impl ser::SerializeStructVariant for SerializeExpressionStructVariant {
     }
 }
 
-pub struct ObjectKeySerializer;
+pub(crate) struct ObjectKeySerializer;
 
 impl ser::Serializer for ObjectKeySerializer {
     type Ok = ObjectKey;
@@ -461,7 +447,7 @@ impl ser::Serializer for ObjectKeySerializer {
         seq tuple tuple_struct tuple_variant map struct struct_variant
     }
 
-    serialize_self! { some newtype_struct }
+    serialize_self! { some newtype_struct newtype_variant }
 
     fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
         Ok(ObjectKey::from(value))
@@ -511,26 +497,4 @@ impl ser::Serializer for ObjectKeySerializer {
     ) -> Result<Self::Ok> {
         Identifier::new(variant).map(ObjectKey::Identifier)
     }
-
-    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", "Expression") => Ok(ObjectKey::Expression(
-                value.serialize(ExpressionSerializer)?,
-            )),
-            (_, _) => value.serialize(self),
-        }
-    }
 }
diff --git a/src/expr/ser/operation.rs b/src/expr/ser/operation.rs
deleted file mode 100644
index 55e2508..0000000
--- a/src/expr/ser/operation.rs
+++ /dev/null
@@ -1,403 +0,0 @@
-use super::ExpressionSerializer;
-use crate::expr::{BinaryOp, BinaryOperator, Expression, Operation, UnaryOp, UnaryOperator};
-use crate::ser::FromStrSerializer;
-use crate::{Error, Result};
-use serde::ser::{self, Impossible, Serialize};
-
-pub struct OperationSerializer;
-
-impl ser::Serializer for OperationSerializer {
-    type Ok = Operation;
-    type Error = Error;
-
-    type SerializeSeq = SerializeOperationSeq;
-    type SerializeTuple = SerializeOperationSeq;
-    type SerializeTupleStruct = SerializeOperationSeq;
-    type SerializeTupleVariant = Impossible<Operation, Error>;
-    type SerializeMap = Impossible<Operation, Error>;
-    type SerializeStruct = SerializeOperationStruct;
-    type SerializeStructVariant = Impossible<Operation, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    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 `Operation` type itself.
-        match (name, variant) {
-            ("$hcl::operation", "Binary") => {
-                Ok(Operation::Binary(value.serialize(BinaryOpSerializer)?))
-            }
-            (_, _) => Ok(Operation::Unary(value.serialize(UnaryOpSerializer)?)),
-        }
-    }
-
-    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeOperationSeq::new(len))
-    }
-
-    fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeOperationStruct::new(name))
-    }
-}
-
-pub enum SerializeOperationSeq {
-    Unary(SerializeUnaryOpSeq),
-    Binary(SerializeBinaryOpSeq),
-}
-
-impl SerializeOperationSeq {
-    fn new(len: Option<usize>) -> Self {
-        // Specialization for the `BinaryOp` type.
-        if let Some(3) = len {
-            SerializeOperationSeq::Binary(SerializeBinaryOpSeq::new())
-        } else {
-            SerializeOperationSeq::Unary(SerializeUnaryOpSeq::new())
-        }
-    }
-}
-
-impl ser::SerializeSeq for SerializeOperationSeq {
-    type Ok = Operation;
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match self {
-            SerializeOperationSeq::Unary(attr) => attr.serialize_element(value),
-            SerializeOperationSeq::Binary(block) => block.serialize_element(value),
-        }
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match self {
-            SerializeOperationSeq::Unary(attr) => attr.end().map(Into::into),
-            SerializeOperationSeq::Binary(block) => block.end().map(Into::into),
-        }
-    }
-}
-
-impl ser::SerializeTuple for SerializeOperationSeq {
-    impl_forward_to_serialize_seq!(serialize_element, Operation);
-}
-
-impl ser::SerializeTupleStruct for SerializeOperationSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Operation);
-}
-
-pub enum SerializeOperationStruct {
-    Unary(SerializeUnaryOpStruct),
-    Binary(SerializeBinaryOpStruct),
-}
-
-impl SerializeOperationStruct {
-    pub fn new(name: &'static str) -> Self {
-        // Specialization for the `UnaryOp` and `BinaryOp` types.
-        match name {
-            "$hcl::binary_op" => SerializeOperationStruct::Binary(SerializeBinaryOpStruct::new()),
-            _ => SerializeOperationStruct::Unary(SerializeUnaryOpStruct::new()),
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeOperationStruct {
-    type Ok = Operation;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match self {
-            SerializeOperationStruct::Unary(ser) => ser.serialize_field(key, value),
-            SerializeOperationStruct::Binary(ser) => ser.serialize_field(key, value),
-        }
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match self {
-            SerializeOperationStruct::Unary(ser) => ser.end().map(Into::into),
-            SerializeOperationStruct::Binary(ser) => ser.end().map(Into::into),
-        }
-    }
-}
-
-pub struct UnaryOpSerializer;
-
-impl ser::Serializer for UnaryOpSerializer {
-    type Ok = UnaryOp;
-    type Error = Error;
-
-    type SerializeSeq = SerializeUnaryOpSeq;
-    type SerializeTuple = SerializeUnaryOpSeq;
-    type SerializeTupleStruct = SerializeUnaryOpSeq;
-    type SerializeTupleVariant = Impossible<UnaryOp, Error>;
-    type SerializeMap = Impossible<UnaryOp, Error>;
-    type SerializeStruct = SerializeUnaryOpStruct;
-    type SerializeStructVariant = Impossible<UnaryOp, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        newtype_variant tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeUnaryOpSeq::new())
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeUnaryOpStruct::new())
-    }
-}
-
-pub struct SerializeUnaryOpSeq {
-    operator: Option<UnaryOperator>,
-    expr: Option<Expression>,
-}
-
-impl SerializeUnaryOpSeq {
-    pub fn new() -> Self {
-        SerializeUnaryOpSeq {
-            operator: None,
-            expr: None,
-        }
-    }
-}
-
-impl ser::SerializeSeq for SerializeUnaryOpSeq {
-    type Ok = UnaryOp;
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if self.operator.is_none() {
-            self.operator = Some(value.serialize(FromStrSerializer::new())?);
-        } 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.operator, self.expr) {
-            (Some(operator), Some(expr)) => Ok(UnaryOp { operator, expr }),
-            (_, _) => Err(ser::Error::custom("expected sequence with 2 elements")),
-        }
-    }
-}
-
-impl ser::SerializeTuple for SerializeUnaryOpSeq {
-    impl_forward_to_serialize_seq!(serialize_element, UnaryOp);
-}
-
-impl ser::SerializeTupleStruct for SerializeUnaryOpSeq {
-    impl_forward_to_serialize_seq!(serialize_field, UnaryOp);
-}
-
-pub struct SerializeUnaryOpStruct {
-    operator: Option<UnaryOperator>,
-    expr: Option<Expression>,
-}
-
-impl SerializeUnaryOpStruct {
-    pub fn new() -> Self {
-        SerializeUnaryOpStruct {
-            operator: None,
-            expr: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeUnaryOpStruct {
-    type Ok = UnaryOp;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "operator" => self.operator = Some(value.serialize(FromStrSerializer::new())?),
-            "expr" => self.expr = Some(value.serialize(ExpressionSerializer)?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `operator` and `expr`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.operator, self.expr) {
-            (Some(operator), Some(expr)) => Ok(UnaryOp { operator, expr }),
-            (_, _) => Err(ser::Error::custom(
-                "expected struct with fields `operator` and `expr`",
-            )),
-        }
-    }
-}
-
-pub struct BinaryOpSerializer;
-
-impl ser::Serializer for BinaryOpSerializer {
-    type Ok = BinaryOp;
-    type Error = Error;
-
-    type SerializeSeq = SerializeBinaryOpSeq;
-    type SerializeTuple = SerializeBinaryOpSeq;
-    type SerializeTupleStruct = SerializeBinaryOpSeq;
-    type SerializeTupleVariant = Impossible<BinaryOp, Error>;
-    type SerializeMap = Impossible<BinaryOp, Error>;
-    type SerializeStruct = SerializeBinaryOpStruct;
-    type SerializeStructVariant = Impossible<BinaryOp, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        newtype_variant tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeBinaryOpSeq::new())
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeBinaryOpStruct::new())
-    }
-}
-
-pub struct SerializeBinaryOpSeq {
-    lhs_expr: Option<Expression>,
-    operator: Option<BinaryOperator>,
-    rhs_expr: Option<Expression>,
-}
-
-impl SerializeBinaryOpSeq {
-    pub fn new() -> Self {
-        SerializeBinaryOpSeq {
-            lhs_expr: None,
-            operator: None,
-            rhs_expr: None,
-        }
-    }
-}
-
-impl ser::SerializeSeq for SerializeBinaryOpSeq {
-    type Ok = BinaryOp;
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if self.lhs_expr.is_none() {
-            self.lhs_expr = Some(value.serialize(ExpressionSerializer)?);
-        } else if self.operator.is_none() {
-            self.operator = Some(value.serialize(FromStrSerializer::new())?);
-        } else if self.rhs_expr.is_none() {
-            self.rhs_expr = Some(value.serialize(ExpressionSerializer)?);
-        } else {
-            return Err(ser::Error::custom("expected sequence with 3 elements"));
-        }
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.lhs_expr, self.operator, self.rhs_expr) {
-            (Some(lhs_expr), Some(operator), Some(rhs_expr)) => Ok(BinaryOp {
-                lhs_expr,
-                operator,
-                rhs_expr,
-            }),
-            (_, _, _) => Err(ser::Error::custom("expected sequence with 3 elements")),
-        }
-    }
-}
-
-impl ser::SerializeTuple for SerializeBinaryOpSeq {
-    impl_forward_to_serialize_seq!(serialize_element, BinaryOp);
-}
-
-impl ser::SerializeTupleStruct for SerializeBinaryOpSeq {
-    impl_forward_to_serialize_seq!(serialize_field, BinaryOp);
-}
-
-pub struct SerializeBinaryOpStruct {
-    lhs_expr: Option<Expression>,
-    operator: Option<BinaryOperator>,
-    rhs_expr: Option<Expression>,
-}
-
-impl SerializeBinaryOpStruct {
-    pub fn new() -> Self {
-        SerializeBinaryOpStruct {
-            lhs_expr: None,
-            operator: None,
-            rhs_expr: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeBinaryOpStruct {
-    type Ok = BinaryOp;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "lhs_expr" => self.lhs_expr = Some(value.serialize(ExpressionSerializer)?),
-            "operator" => self.operator = Some(value.serialize(FromStrSerializer::new())?),
-            "rhs_expr" => self.rhs_expr = Some(value.serialize(ExpressionSerializer)?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `lhs_expr`, `operator` and `rhs_expr`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.lhs_expr, self.operator, self.rhs_expr) {
-            (Some(lhs_expr), Some(operator), Some(rhs_expr)) => Ok(BinaryOp {
-                lhs_expr,
-                operator,
-                rhs_expr,
-            }),
-            (_, _, _) => Err(ser::Error::custom(
-                "expected struct with fields `lhs_expr`, `operator` and `rhs_expr`",
-            )),
-        }
-    }
-}
diff --git a/src/expr/ser/template_expr.rs b/src/expr/ser/template_expr.rs
deleted file mode 100644
index 9515527..0000000
--- a/src/expr/ser/template_expr.rs
+++ /dev/null
@@ -1,308 +0,0 @@
-use crate::expr::{Heredoc, HeredocStripMode, TemplateExpr};
-use crate::ser::{FromStrSerializer, IdentifierSerializer, StringSerializer};
-use crate::{Error, Identifier, Result};
-use serde::ser::{self, Impossible, Serialize};
-use std::fmt::Display;
-
-pub struct TemplateExprSerializer;
-
-impl ser::Serializer for TemplateExprSerializer {
-    type Ok = TemplateExpr;
-    type Error = Error;
-
-    type SerializeSeq = Impossible<TemplateExpr, Error>;
-    type SerializeTuple = Impossible<TemplateExpr, Error>;
-    type SerializeTupleStruct = Impossible<TemplateExpr, Error>;
-    type SerializeTupleVariant = Impossible<TemplateExpr, Error>;
-    type SerializeMap = Impossible<TemplateExpr, Error>;
-    type SerializeStruct = SerializeTemplateExprStruct;
-    type SerializeStructVariant = Impossible<TemplateExpr, Error>;
-
-    serialize_unsupported! {
-        i8 i16 i32 i64 u8 u16 u32 u64
-        bool f32 f64 bytes unit unit_struct none
-        seq tuple tuple_struct tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    fn serialize_char(self, value: char) -> Result<Self::Ok> {
-        Ok(TemplateExpr::QuotedString(value.to_string()))
-    }
-
-    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
-        Ok(TemplateExpr::QuotedString(value.to_owned()))
-    }
-
-    fn serialize_unit_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<Self::Ok> {
-        Ok(TemplateExpr::QuotedString(variant.to_owned()))
-    }
-
-    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 `TemplateExpr` type itself.
-        match (name, variant) {
-            ("$hcl::template_expr", "QuotedString") => Ok(TemplateExpr::QuotedString(
-                value.serialize(StringSerializer)?,
-            )),
-            ("$hcl::template_expr", "Heredoc") => {
-                Ok(TemplateExpr::Heredoc(value.serialize(HeredocSerializer)?))
-            }
-            (_, _) => value.serialize(self),
-        }
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeTemplateExprStruct::new())
-    }
-
-    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
-    where
-        T: ?Sized + Display,
-    {
-        Ok(TemplateExpr::QuotedString(value.to_string()))
-    }
-}
-
-pub struct SerializeTemplateExprStruct {
-    inner: SerializeHeredocStruct,
-}
-
-impl SerializeTemplateExprStruct {
-    pub fn new() -> Self {
-        SerializeTemplateExprStruct {
-            inner: SerializeHeredocStruct::new(),
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeTemplateExprStruct {
-    impl_forward_to_inner!(TemplateExpr, serialize_field(key: &'static str));
-}
-
-pub struct HeredocSerializer;
-
-impl ser::Serializer for HeredocSerializer {
-    type Ok = Heredoc;
-    type Error = Error;
-
-    type SerializeSeq = SerializeHeredocSeq;
-    type SerializeTuple = SerializeHeredocSeq;
-    type SerializeTupleStruct = SerializeHeredocSeq;
-    type SerializeTupleVariant = Impossible<Heredoc, Error>;
-    type SerializeMap = SerializeHeredocMap;
-    type SerializeStruct = SerializeHeredocStruct;
-    type SerializeStructVariant = Impossible<Heredoc, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        tuple_variant struct_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        value: &T,
-    ) -> Result<Heredoc>
-    where
-        T: ?Sized + Serialize,
-    {
-        Ok(Heredoc {
-            delimiter: Identifier::new(variant)?,
-            template: value.serialize(StringSerializer)?,
-            strip: HeredocStripMode::None,
-        })
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeHeredocSeq::new())
-    }
-
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Ok(SerializeHeredocMap::new())
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeHeredocStruct::new())
-    }
-}
-
-pub struct SerializeHeredocSeq {
-    delimiter: Option<Identifier>,
-    template: Option<String>,
-    strip: Option<HeredocStripMode>,
-}
-
-impl SerializeHeredocSeq {
-    pub fn new() -> Self {
-        SerializeHeredocSeq {
-            delimiter: None,
-            template: None,
-            strip: None,
-        }
-    }
-}
-
-impl ser::SerializeSeq for SerializeHeredocSeq {
-    type Ok = Heredoc;
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if self.delimiter.is_none() {
-            self.delimiter = Some(value.serialize(IdentifierSerializer)?);
-        } else if self.template.is_none() {
-            self.template = Some(value.serialize(StringSerializer)?);
-        } else if self.strip.is_none() {
-            self.strip = Some(value.serialize(FromStrSerializer::new())?);
-        } else {
-            return Err(ser::Error::custom("expected sequence with 2 or 3 elements"));
-        }
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.delimiter, self.template) {
-            (Some(delimiter), Some(template)) => Ok(Heredoc {
-                delimiter,
-                template,
-                strip: self.strip.unwrap_or(HeredocStripMode::None),
-            }),
-            (_, _) => Err(ser::Error::custom("expected sequence with 2 or 3 elements")),
-        }
-    }
-}
-
-impl ser::SerializeTuple for SerializeHeredocSeq {
-    impl_forward_to_serialize_seq!(serialize_element, Heredoc);
-}
-
-impl serde::ser::SerializeTupleStruct for SerializeHeredocSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Heredoc);
-}
-
-pub struct SerializeHeredocMap {
-    delimiter: Option<Identifier>,
-    template: Option<String>,
-}
-
-impl SerializeHeredocMap {
-    pub fn new() -> Self {
-        SerializeHeredocMap {
-            delimiter: None,
-            template: None,
-        }
-    }
-}
-
-impl ser::SerializeMap for SerializeHeredocMap {
-    type Ok = Heredoc;
-    type Error = Error;
-
-    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        if self.delimiter.is_none() {
-            self.delimiter = Some(key.serialize(IdentifierSerializer)?);
-            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,
-    {
-        assert!(
-            self.template.is_some(),
-            "serialize_value called before serialize_key"
-        );
-        self.template = Some(value.serialize(StringSerializer)?);
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.delimiter, self.template) {
-            (Some(delimiter), Some(template)) => Ok(Heredoc {
-                delimiter,
-                template,
-                strip: HeredocStripMode::None,
-            }),
-            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
-        }
-    }
-}
-
-pub struct SerializeHeredocStruct {
-    delimiter: Option<Identifier>,
-    template: Option<String>,
-    strip: Option<HeredocStripMode>,
-}
-
-impl SerializeHeredocStruct {
-    pub fn new() -> Self {
-        SerializeHeredocStruct {
-            delimiter: None,
-            template: None,
-            strip: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeHeredocStruct {
-    type Ok = Heredoc;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "delimiter" => self.delimiter = Some(value.serialize(IdentifierSerializer)?),
-            "template" => self.template = Some(value.serialize(StringSerializer)?),
-            "strip" => self.strip = Some(value.serialize(FromStrSerializer::new())?),
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `delimiter`, `template` and optional `strip`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.delimiter, self.template) {
-            (Some(delimiter), Some(template)) => Ok(Heredoc {
-                delimiter,
-                template,
-                strip: self.strip.unwrap_or(HeredocStripMode::None),
-            }),
-            (_, _) => Err(ser::Error::custom(
-                "expected struct with fields `delimiter`, `template` and optional `strip`",
-            )),
-        }
-    }
-}
diff --git a/src/expr/ser/traversal.rs b/src/expr/ser/traversal.rs
deleted file mode 100644
index 6dc8900..0000000
--- a/src/expr/ser/traversal.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-use super::ExpressionSerializer;
-use crate::expr::{Expression, Traversal, TraversalOperator};
-use crate::ser::{IdentifierSerializer, SeqSerializer};
-use crate::{Error, Identifier, Result};
-use serde::ser::{self, Impossible, Serialize};
-
-pub struct TraversalSerializer;
-
-impl ser::Serializer for TraversalSerializer {
-    type Ok = Traversal;
-    type Error = Error;
-
-    type SerializeSeq = Impossible<Traversal, Error>;
-    type SerializeTuple = Impossible<Traversal, Error>;
-    type SerializeTupleStruct = Impossible<Traversal, Error>;
-    type SerializeTupleVariant = Impossible<Traversal, Error>;
-    type SerializeMap = Impossible<Traversal, Error>;
-    type SerializeStruct = SerializeTraversalStruct;
-    type SerializeStructVariant = Impossible<Traversal, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-        newtype_variant seq tuple tuple_struct tuple_variant map struct_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SerializeTraversalStruct::new())
-    }
-}
-
-pub struct SerializeTraversalStruct {
-    expr: Option<Expression>,
-    operators: Option<Vec<TraversalOperator>>,
-}
-
-impl SerializeTraversalStruct {
-    pub fn new() -> Self {
-        SerializeTraversalStruct {
-            expr: None,
-            operators: None,
-        }
-    }
-}
-
-impl ser::SerializeStruct for SerializeTraversalStruct {
-    type Ok = Traversal;
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + ser::Serialize,
-    {
-        match key {
-            "expr" => self.expr = Some(value.serialize(ExpressionSerializer)?),
-            "operators" => {
-                self.operators =
-                    Some(value.serialize(SeqSerializer::new(TraversalOperatorSerializer))?);
-            }
-            _ => {
-                return Err(ser::Error::custom(
-                    "expected struct with fields `expr` and `operators`",
-                ))
-            }
-        };
-
-        Ok(())
-    }
-
-    fn end(self) -> Result<Self::Ok> {
-        match (self.expr, self.operators) {
-            (Some(expr), Some(operators)) => Ok(Traversal { expr, operators }),
-            (_, _) => Err(ser::Error::custom(
-                "expected struct with fields `expr` and `operators`",
-            )),
-        }
-    }
-}
-
-#[derive(Clone)]
-struct TraversalOperatorSerializer;
-
-impl ser::Serializer for TraversalOperatorSerializer {
-    type Ok = TraversalOperator;
-    type Error = Error;
-
-    type SerializeSeq = Impossible<TraversalOperator, Error>;
-    type SerializeTuple = Impossible<TraversalOperator, Error>;
-    type SerializeTupleStruct = Impossible<TraversalOperator, Error>;
-    type SerializeTupleVariant = Impossible<TraversalOperator, Error>;
-    type SerializeMap = Impossible<TraversalOperator, Error>;
-    type SerializeStruct = Impossible<TraversalOperator, Error>;
-    type SerializeStructVariant = Impossible<TraversalOperator, Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 f32 f64
-        char str bytes none unit unit_struct
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-    serialize_self! { some }
-
-    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
-        Ok(TraversalOperator::LegacyIndex(v))
-    }
-
-    fn serialize_unit_variant(
-        self,
-        name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<Self::Ok> {
-        match (name, variant) {
-            ("$hcl::traversal_operator", "AttrSplat") => Ok(TraversalOperator::AttrSplat),
-            ("$hcl::traversal_operator", "FullSplat") => Ok(TraversalOperator::FullSplat),
-            (_, _) => Identifier::new(variant).map(TraversalOperator::GetAttr),
-        }
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        name: &'static str,
-        _variant_index: u32,
-        _variant: &'static str,
-        value: &T,
-    ) -> Result<Self::Ok>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::expression" {
-            Ok(TraversalOperator::Index(
-                value.serialize(ExpressionSerializer)?,
-            ))
-        } else {
-            value.serialize(self)
-        }
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::identifier" {
-            Ok(TraversalOperator::GetAttr(
-                value.serialize(IdentifierSerializer)?,
-            ))
-        } else {
-            value.serialize(self)
-        }
-    }
-}
diff --git a/src/expr/template_expr.rs b/src/expr/template_expr.rs
index d23aea2..321fbbb 100644
--- a/src/expr/template_expr.rs
+++ b/src/expr/template_expr.rs
@@ -1,7 +1,6 @@
-use crate::de::FromStrVisitor;
 use crate::util::{dedent, try_unescape};
 use crate::{Error, Identifier, Result};
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 use std::borrow::Cow;
 use std::fmt;
 use std::str::FromStr;
@@ -11,8 +10,7 @@ use std::str::FromStr;
 /// This type wraps the raw template string representation. Refer to the documentation of the
 /// [`template`][`crate::template`] module if you need to parse and further evaluate the raw
 /// template.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::template_expr")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
 pub enum TemplateExpr {
     /// A quoted template expression is delimited by quote characters (`"`) and defines a template
     /// as a single-line expression with escape characters. The raw template string may contain
@@ -61,8 +59,7 @@ impl fmt::Display for TemplateExpr {
 
 /// A heredoc template expression is introduced by a `<<` sequence and defines a template via a
 /// multi-line sequence terminated by a user-chosen delimiter.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::heredoc")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
 pub struct Heredoc {
     /// The delimiter identifier that denotes the heredoc start and end.
     pub delimiter: Identifier,
@@ -140,21 +137,3 @@ impl FromStr for HeredocStripMode {
         }
     }
 }
-
-impl Serialize for HeredocStripMode {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        serializer.serialize_str(self.as_str())
-    }
-}
-
-impl<'de> Deserialize<'de> for HeredocStripMode {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(FromStrVisitor::<Self>::new("a heredoc strip mode"))
-    }
-}
diff --git a/src/expr/traversal.rs b/src/expr/traversal.rs
index 7b5a345..e9bc13e 100644
--- a/src/expr/traversal.rs
+++ b/src/expr/traversal.rs
@@ -3,8 +3,7 @@ use crate::Identifier;
 use serde::{Deserialize, Serialize};
 
 /// Traverse an expression to access attributes, object keys or element indices.
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::traversal")]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
 pub struct Traversal {
     /// The expression that the access operator is applied to.
     pub expr: Expression,
@@ -107,7 +106,6 @@ impl TraversalBuilder {
 
 /// The expression traversal operators that are supported by HCL.
 #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
-#[serde(rename = "$hcl::traversal_operator")]
 pub enum TraversalOperator {
     /// The attribute-only splat operator supports only attribute lookups into the elements from a
     /// list, but supports an arbitrary number of them.
diff --git a/src/expr/variable.rs b/src/expr/variable.rs
index 3271e69..81b892c 100644
--- a/src/expr/variable.rs
+++ b/src/expr/variable.rs
@@ -1,5 +1,5 @@
 use crate::{Identifier, Result};
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 use std::ops::Deref;
 
 /// A type representing a variable in an HCL expression.
@@ -9,7 +9,8 @@ use std::ops::Deref;
 ///
 /// This is a separate type to differentiate between bare identifiers and variable identifiers
 /// which have different semantics in different scopes.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
+#[serde(transparent)]
 pub struct Variable(Identifier);
 
 impl Variable {
@@ -76,21 +77,3 @@ impl From<Identifier> for Variable {
         Variable(ident)
     }
 }
-
-impl Serialize for Variable {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        self.0.serialize(serializer)
-    }
-}
-
-impl<'de> Deserialize<'de> for Variable {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        Identifier::deserialize(deserializer).map(Variable)
-    }
-}
diff --git a/src/format/impls.rs b/src/format/impls.rs
index df8ef26..58db678 100644
--- a/src/format/impls.rs
+++ b/src/format/impls.rs
@@ -1,7 +1,7 @@
 use super::{private, Format, Formatter};
 use crate::expr::{
     BinaryOp, Conditional, Expression, ForExpr, FuncCall, Heredoc, HeredocStripMode, ObjectKey,
-    Operation, RawExpression, TemplateExpr, Traversal, TraversalOperator, UnaryOp,
+    Operation, RawExpression, TemplateExpr, Traversal, TraversalOperator, UnaryOp, Variable,
 };
 use crate::structure::{Attribute, Block, BlockLabel, Body, Structure};
 use crate::template::{
@@ -120,7 +120,7 @@ impl Format for Expression {
             Expression::Object(object) => format_object(fmt, object.iter()),
             Expression::Raw(raw) => raw.format(fmt),
             Expression::TemplateExpr(expr) => expr.format(fmt),
-            Expression::Variable(ident) => ident.format(fmt),
+            Expression::Variable(var) => var.format(fmt),
             Expression::Traversal(traversal) => traversal.format(fmt),
             Expression::FuncCall(func_call) => func_call.format(fmt),
             Expression::Parenthesis(expr) => {
@@ -255,6 +255,17 @@ impl Format for Identifier {
     }
 }
 
+impl private::Sealed for Variable {}
+
+impl Format for Variable {
+    fn format<W>(&self, fmt: &mut Formatter<W>) -> Result<()>
+    where
+        W: io::Write,
+    {
+        fmt.write_string_fragment(self)
+    }
+}
+
 impl private::Sealed for Traversal {}
 
 impl Format for Traversal {
diff --git a/src/format/mod.rs b/src/format/mod.rs
index e95293c..de536f0 100644
--- a/src/format/mod.rs
+++ b/src/format/mod.rs
@@ -624,7 +624,7 @@ where
 ///
 /// # Errors
 ///
-/// Formatting fails if the value contains strings that cannot be used as valid HCL identifiers.
+/// Formatting a value as byte vector cannot fail.
 pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
 where
     T: ?Sized + Format,
@@ -641,7 +641,7 @@ where
 ///
 /// # Errors
 ///
-/// Formatting fails if the value contains strings that cannot be used as valid HCL identifiers.
+/// Formatting a value as string cannot fail.
 pub fn to_string<T>(value: &T) -> Result<String>
 where
     T: ?Sized + Format,
@@ -661,8 +661,7 @@ where
 ///
 /// # Errors
 ///
-/// Formatting fails if any operation on the writer fails or if the value contains strings that
-/// cannot be used as valid HCL identifiers.
+/// Formatting fails if any operation on the writer fails.
 pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
 where
     W: io::Write,
@@ -671,3 +670,30 @@ where
     let mut formatter = Formatter::new(writer);
     value.format(&mut formatter)
 }
+
+/// Format the given value as an interpolated HCL string.
+///
+/// It is the callers responsiblity to ensure that the value is not an HCL structure (i.e. `Body`,
+/// `Structure`, `Block` or `Attribute`). Otherwise this will produce invalid HCL.
+///
+/// # Errors
+///
+/// Formatting a value as string cannot fail.
+pub(crate) fn to_interpolated_string<T>(value: &T) -> Result<String>
+where
+    T: ?Sized + Format,
+{
+    let mut buf = Vec::with_capacity(128);
+    buf.push(b'$');
+    buf.push(b'{');
+
+    let mut formatter = Formatter::builder().compact(true).build(&mut buf);
+    value.format(&mut formatter)?;
+
+    let mut string = unsafe {
+        // We do not emit invalid UTF-8.
+        String::from_utf8_unchecked(buf)
+    };
+    string.push('}');
+    Ok(string)
+}
diff --git a/src/ident.rs b/src/ident.rs
index dc9f1b9..3e6b17f 100644
--- a/src/ident.rs
+++ b/src/ident.rs
@@ -8,7 +8,7 @@ use std::ops;
 
 /// Represents an HCL identifier.
 #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)]
-#[serde(rename = "$hcl::identifier")]
+#[serde(transparent)]
 pub struct Identifier(String);
 
 impl Identifier {
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index b762dc8..236a533 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -194,20 +194,38 @@
 mod tests;
 
 use crate::format::{Format, Formatter};
-use crate::structure::ser::{
-    BodySerializer, SerializeBodyMap, SerializeBodySeq, SerializeBodyStruct,
-    SerializeBodyStructVariant, SerializeBodyTupleVariant,
-};
+use crate::structure::Body;
 use crate::{Error, Identifier, Result};
-use serde::ser::{self, Impossible, Serialize};
+use serde::ser::{self, Impossible, Serialize, SerializeStruct};
+use std::cell::RefCell;
+use std::collections::BTreeMap;
 use std::fmt;
 use std::io;
-use std::marker::PhantomData;
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
 
 // Deprecated, this re-export will be removed in a future release.
 #[doc(hidden)]
 pub use crate::expr::to_expression;
 
+thread_local! {
+    static INTERNAL_SERIALIZATION: AtomicBool = AtomicBool::new(false);
+}
+
+pub(crate) fn in_internal_serialization() -> bool {
+    INTERNAL_SERIALIZATION.with(|flag| flag.load(Ordering::Relaxed))
+}
+
+pub(crate) fn with_internal_serialization<R, F: FnOnce() -> R>(f: F) -> R {
+    INTERNAL_SERIALIZATION.with(|flag| {
+        let old = flag.load(Ordering::Relaxed);
+        flag.store(true, Ordering::Relaxed);
+        let _on_drop = OnDrop::new(|| {
+            flag.store(old, Ordering::Relaxed);
+        });
+        f()
+    })
+}
+
 /// A structure for serializing Rust values into HCL.
 pub struct Serializer<'a, W> {
     formatter: Formatter<'a, W>,
@@ -233,201 +251,18 @@ where
         self.formatter.into_inner()
     }
 
-    fn format<V>(&mut self, value: V) -> Result<()>
-    where
-        V: Format,
-    {
-        value.format(&mut self.formatter)
-    }
-}
-
-impl<'a, W> ser::Serializer for &'a mut Serializer<'a, W>
-where
-    W: io::Write,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = SerializeSeq<'a, W>;
-    type SerializeTuple = SerializeSeq<'a, W>;
-    type SerializeTupleStruct = SerializeSeq<'a, W>;
-    type SerializeTupleVariant = SerializeTupleVariant<'a, W>;
-    type SerializeMap = SerializeMap<'a, W>;
-    type SerializeStruct = SerializeStruct<'a, W>;
-    type SerializeStructVariant = SerializeStructVariant<'a, W>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        char str bytes none unit unit_struct unit_variant
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        name: &'static str,
-        variant_index: u32,
-        variant: &'static str,
-        value: &T,
-    ) -> Result<()>
+    /// Serialize the given value as HCL via the serializer's `Formatter` to the underlying writer.
+    ///
+    /// # Errors
+    ///
+    /// Serialization fails if the type cannot be represented as HCL.
+    pub fn serialize<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        BodySerializer
-            .serialize_newtype_variant(name, variant_index, variant, value)
-            .and_then(|body| self.format(body))
-    }
-
-    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        BodySerializer
-            .serialize_seq(len)
-            .map(|inner| SerializeSeq { ser: self, inner })
+        let body = Body::from_serializable(value)?;
+        body.format(&mut self.formatter)
     }
-
-    fn serialize_tuple_variant(
-        self,
-        name: &'static str,
-        variant_index: u32,
-        variant: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        BodySerializer
-            .serialize_tuple_variant(name, variant_index, variant, len)
-            .map(|inner| SerializeTupleVariant { ser: self, inner })
-    }
-
-    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
-        BodySerializer
-            .serialize_map(len)
-            .map(|inner| SerializeMap { ser: self, inner })
-    }
-
-    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
-        BodySerializer
-            .serialize_struct(name, len)
-            .map(|inner| SerializeStruct { ser: self, inner })
-    }
-
-    fn serialize_struct_variant(
-        self,
-        name: &'static str,
-        variant_index: u32,
-        variant: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeStructVariant> {
-        BodySerializer
-            .serialize_struct_variant(name, variant_index, variant, len)
-            .map(|inner| SerializeStructVariant { ser: self, inner })
-    }
-}
-
-macro_rules! impl_forward_to_inner {
-    ($($tt:tt)+) => {
-        type Ok = ();
-        type Error = $crate::Error;
-
-        impl_forward_to_inner_internal!($($tt)+);
-
-        fn end(self) -> $crate::Result<Self::Ok, Self::Error> {
-            self.inner.end().and_then(|body| self.ser.format(body))
-        }
-    };
-}
-
-macro_rules! impl_forward_to_inner_internal {
-    ($method:ident($($arg:ident: $ty:ty),*) $(,$rest:tt)*) => {
-        fn $method<T>(&mut self, $($arg: $ty,)* value: &T) -> $crate::Result<(), Self::Error>
-        where
-            T: ?Sized + serde::ser::Serialize,
-        {
-            self.inner.$method($($arg,)* value)
-        }
-
-        impl_forward_to_inner_internal!($($rest),*);
-    };
-    ($method:ident $(,$rest:tt)*) => {
-        impl_forward_to_inner_internal!($method() $(,$rest)*);
-    };
-    () => {};
-}
-
-#[doc(hidden)]
-pub struct SerializeSeq<'a, W> {
-    inner: SerializeBodySeq,
-    ser: &'a mut Serializer<'a, W>,
-}
-
-impl<'a, W> ser::SerializeSeq for SerializeSeq<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_element);
-}
-
-impl<'a, W> ser::SerializeTuple for SerializeSeq<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_element);
-}
-
-impl<'a, W> ser::SerializeTupleStruct for SerializeSeq<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_field);
-}
-
-#[doc(hidden)]
-pub struct SerializeTupleVariant<'a, W> {
-    inner: SerializeBodyTupleVariant,
-    ser: &'a mut Serializer<'a, W>,
-}
-
-impl<'a, W> ser::SerializeTupleVariant for SerializeTupleVariant<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_field);
-}
-
-#[doc(hidden)]
-pub struct SerializeMap<'a, W> {
-    inner: SerializeBodyMap,
-    ser: &'a mut Serializer<'a, W>,
-}
-
-impl<'a, W> ser::SerializeMap for SerializeMap<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_key, serialize_value);
-}
-
-#[doc(hidden)]
-pub struct SerializeStructVariant<'a, W> {
-    inner: SerializeBodyStructVariant,
-    ser: &'a mut Serializer<'a, W>,
-}
-
-impl<'a, W> ser::SerializeStructVariant for SerializeStructVariant<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_field(key: &'static str));
-}
-
-#[doc(hidden)]
-pub struct SerializeStruct<'a, W> {
-    inner: SerializeBodyStruct,
-    ser: &'a mut Serializer<'a, W>,
-}
-
-impl<'a, W> ser::SerializeStruct for SerializeStruct<'a, W>
-where
-    W: io::Write,
-{
-    impl_forward_to_inner!(serialize_field(key: &'static str));
 }
 
 /// Serialize the given value as an HCL byte vector.
@@ -485,7 +320,7 @@ where
     T: ?Sized + Serialize,
 {
     let mut serializer = Serializer::new(writer);
-    value.serialize(&mut serializer)
+    serializer.serialize(value)
 }
 
 pub(crate) struct StringSerializer;
@@ -573,213 +408,105 @@ impl ser::Serializer for IdentifierSerializer {
     }
 }
 
-pub(crate) struct BoolSerializer;
+struct U64Serializer;
 
-impl ser::Serializer for BoolSerializer {
-    type Ok = bool;
+impl ser::Serializer for U64Serializer {
+    type Ok = u64;
     type Error = Error;
 
-    type SerializeSeq = Impossible<bool, Error>;
-    type SerializeTuple = Impossible<bool, Error>;
-    type SerializeTupleStruct = Impossible<bool, Error>;
-    type SerializeTupleVariant = Impossible<bool, Error>;
-    type SerializeMap = Impossible<bool, Error>;
-    type SerializeStruct = Impossible<bool, Error>;
-    type SerializeStructVariant = Impossible<bool, Error>;
+    type SerializeSeq = Impossible<u64, Error>;
+    type SerializeTuple = Impossible<u64, Error>;
+    type SerializeTupleStruct = Impossible<u64, Error>;
+    type SerializeTupleVariant = Impossible<u64, Error>;
+    type SerializeMap = Impossible<u64, Error>;
+    type SerializeStruct = Impossible<u64, Error>;
+    type SerializeStructVariant = Impossible<u64, Error>;
 
     serialize_unsupported! {
-        i8 i16 i32 i64 u8 u16 u32 u64
-        f32 f64 char str bytes unit unit_struct unit_variant newtype_variant none
-        seq tuple tuple_struct tuple_variant map struct struct_variant
+        i8 i16 i32 i64 u8 u16 u32 f32 f64 char str bool bytes
+        unit unit_variant unit_struct newtype_struct newtype_variant
+        some none seq tuple tuple_struct tuple_variant map struct struct_variant
     }
-    serialize_self! { some newtype_struct }
 
-    fn serialize_bool(self, value: bool) -> Result<Self::Ok> {
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
         Ok(value)
     }
 }
 
-pub(crate) struct SeqSerializer<S> {
-    inner: S,
-}
-
-impl<S> SeqSerializer<S> {
-    pub(crate) 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 = SerializeInnerSeq<S>;
-    type SerializeTuple = SerializeInnerSeq<S>;
-    type SerializeTupleStruct = SerializeInnerSeq<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
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
-        Ok(SerializeInnerSeq::new(self.inner, len))
-    }
+pub(crate) struct SerializeInternalHandleStruct {
+    handle: Option<u64>,
 }
 
-pub(crate) struct SerializeInnerSeq<S>
-where
-    S: ser::Serializer,
-{
-    inner: S,
-    vec: Vec<S::Ok>,
-}
-
-impl<S> SerializeInnerSeq<S>
-where
-    S: ser::Serializer,
-{
-    fn new(inner: S, len: Option<usize>) -> Self {
-        SerializeInnerSeq {
-            inner,
-            vec: Vec::with_capacity(len.unwrap_or(0)),
-        }
+impl SerializeInternalHandleStruct {
+    pub(crate) fn new() -> Self {
+        SerializeInternalHandleStruct { handle: None }
     }
 }
 
-impl<S> ser::SerializeSeq for SerializeInnerSeq<S>
-where
-    S: ser::Serializer + Clone,
-{
-    type Ok = Vec<S::Ok>;
-    type Error = S::Error;
+impl ser::SerializeStruct for SerializeInternalHandleStruct {
+    type Ok = usize;
+    type Error = Error;
 
-    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(value.serialize(self.inner.clone())?);
+        assert_eq!(key, "handle", "bad handle struct");
+        self.handle = Some(value.serialize(U64Serializer)?);
         Ok(())
     }
 
-    fn end(self) -> Result<Self::Ok, Self::Error> {
-        Ok(self.vec)
+    fn end(self) -> Result<Self::Ok> {
+        let handle = self.handle.expect("bad handle reference in roundtrip");
+        Ok(handle as usize)
     }
 }
 
-impl<S> ser::SerializeTuple for SerializeInnerSeq<S>
-where
-    S: ser::Serializer + Clone,
-{
-    impl_forward_to_serialize_seq!(serialize_element, Vec<S::Ok>, S::Error);
+pub(crate) struct InternalHandles<T> {
+    marker: &'static str,
+    last_handle: AtomicUsize,
+    handles: RefCell<BTreeMap<usize, T>>,
 }
 
-impl<S> serde::ser::SerializeTupleStruct for SerializeInnerSeq<S>
-where
-    S: ser::Serializer + Clone,
-{
-    impl_forward_to_serialize_seq!(serialize_field, Vec<S::Ok>, S::Error);
-}
-
-pub(crate) struct OptionSerializer<S> {
-    inner: S,
-}
-
-impl<S> OptionSerializer<S> {
-    pub(crate) fn new(inner: S) -> Self {
-        OptionSerializer { inner }
+impl<T> InternalHandles<T> {
+    pub(crate) fn new(marker: &'static str) -> InternalHandles<T> {
+        InternalHandles {
+            marker,
+            last_handle: AtomicUsize::new(0),
+            handles: RefCell::new(BTreeMap::new()),
+        }
     }
-}
 
-impl<S> ser::Serializer for OptionSerializer<S>
-where
-    S: ser::Serializer,
-{
-    type Ok = Option<S::Ok>;
-    type Error = S::Error;
-
-    type SerializeSeq = Impossible<Self::Ok, Self::Error>;
-    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
-    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
-    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 unit newtype_struct newtype_variant unit_struct unit_variant
-        seq tuple tuple_struct tuple_variant map struct struct_variant
+    pub(crate) fn remove(&self, handle: usize) -> T {
+        self.handles
+            .borrow_mut()
+            .remove(&handle)
+            .expect("handle not in registry")
     }
 
-    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    pub(crate) fn serialize<V, S>(&self, value: V, serializer: S) -> Result<S::Ok, S::Error>
     where
-        T: ?Sized + ser::Serialize,
+        S: serde::Serializer,
+        V: Into<T>,
     {
-        Ok(Some(value.serialize(self.inner)?))
-    }
-
-    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
-        Ok(None)
+        let handle = self.last_handle.fetch_add(1, Ordering::Relaxed);
+        self.handles.borrow_mut().insert(handle, value.into());
+        let mut s = serializer.serialize_struct(self.marker, 1)?;
+        s.serialize_field("handle", &handle)?;
+        s.end()
     }
 }
 
-pub(crate) struct FromStrSerializer<O> {
-    marker: PhantomData<O>,
-}
+struct OnDrop<F: FnOnce()>(Option<F>);
 
-impl<O> FromStrSerializer<O> {
-    pub(crate) fn new() -> Self {
-        FromStrSerializer {
-            marker: PhantomData,
-        }
+impl<F: FnOnce()> OnDrop<F> {
+    fn new(f: F) -> Self {
+        Self(Some(f))
     }
 }
 
-impl<O> ser::Serializer for FromStrSerializer<O>
-where
-    O: std::str::FromStr,
-    O::Err: ser::Error,
-{
-    type Ok = O;
-    type Error = O::Err;
-
-    type SerializeSeq = Impossible<Self::Ok, Self::Error>;
-    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
-    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
-    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! {
-        i8 i16 i32 i64 u8 u16 u32 u64
-        bool f32 f64 char bytes unit unit_struct none
-        seq tuple tuple_struct tuple_variant map struct struct_variant
-        newtype_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
-        O::from_str(value)
-    }
-
-    fn serialize_unit_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<Self::Ok, Self::Error> {
-        self.serialize_str(variant)
+impl<F: FnOnce()> Drop for OnDrop<F> {
+    fn drop(&mut self) {
+        self.0.take().unwrap()();
     }
 }
diff --git a/src/structure/attribute.rs b/src/structure/attribute.rs
index 690f70f..5ac445f 100644
--- a/src/structure/attribute.rs
+++ b/src/structure/attribute.rs
@@ -2,7 +2,7 @@
 
 use crate::expr::Expression;
 use crate::{Identifier, Value};
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 use std::iter;
 
 /// Represents an HCL attribute which consists of an attribute key and a value expression.
@@ -15,8 +15,7 @@ use std::iter;
 ///
 /// Use [`Attribute::new`] to construct an [`Attribute`] from a value that is convertible to this
 /// crate's [`Expression`] type.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::attribute")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct Attribute {
     /// The HCL attribute's key.
     pub key: Identifier,
diff --git a/src/structure/block.rs b/src/structure/block.rs
index 1615685..a383522 100644
--- a/src/structure/block.rs
+++ b/src/structure/block.rs
@@ -14,8 +14,7 @@ use serde::{Deserialize, Serialize};
 ///   body
 /// }
 /// ```
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::block")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub struct Block {
     /// The block identifier.
     pub identifier: Identifier,
@@ -105,7 +104,6 @@ where
 /// }
 /// ```
 #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::block_label")]
 pub enum BlockLabel {
     /// A bare HCL block label.
     Identifier(Identifier),
diff --git a/src/structure/body.rs b/src/structure/body.rs
index dd9c501..37176c7 100644
--- a/src/structure/body.rs
+++ b/src/structure/body.rs
@@ -1,6 +1,9 @@
 //! Types to represent and build HCL body structures.
 
+use super::ser::BodySerializer;
 use super::{Attribute, Block, IntoNodeMap, Map, Structure, Value};
+use crate::ser::with_internal_serialization;
+use crate::Result;
 use serde::{Deserialize, Serialize};
 use std::vec::IntoIter;
 
@@ -8,10 +11,18 @@ use std::vec::IntoIter;
 ///
 /// A `Body` consists of zero or more [`Attribute`] and [`Block`] HCL structures.
 #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Default, Clone)]
-#[serde(rename = "$hcl::body")]
+#[serde(rename = "$hcl::Body")]
 pub struct Body(pub Vec<Structure>);
 
 impl Body {
+    #[doc(hidden)]
+    pub fn from_serializable<T>(value: &T) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        with_internal_serialization(|| value.serialize(BodySerializer))
+    }
+
     /// Consumes `self` and returns the wrapped `Vec<Structure>`.
     pub fn into_inner(self) -> Vec<Structure> {
         self.0
diff --git a/src/structure/mod.rs b/src/structure/mod.rs
index 32494ba..5637a0e 100644
--- a/src/structure/mod.rs
+++ b/src/structure/mod.rs
@@ -49,7 +49,7 @@ mod attribute;
 mod block;
 mod body;
 pub(crate) mod de;
-pub(crate) mod ser;
+mod ser;
 #[cfg(test)]
 mod tests;
 
@@ -59,13 +59,12 @@ pub use self::{
     body::{Body, BodyBuilder},
 };
 use crate::{Map, Value};
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 
 /// Represents an HCL structure.
 ///
 /// There are two possible structures that can occur in an HCL [`Body`]: [`Attribute`]s and [`Block`]s.
-#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
-#[serde(rename = "$hcl::structure")]
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
 pub enum Structure {
     /// Represents an HCL attribute.
     Attribute(Attribute),
diff --git a/src/structure/ser/attribute.rs b/src/structure/ser/attribute.rs
deleted file mode 100644
index 11b478d..0000000
--- a/src/structure/ser/attribute.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-use crate::expr::ser::ExpressionSerializer;
-use crate::expr::{Expression, Object, ObjectKey};
-use crate::ser::IdentifierSerializer;
-use crate::structure::Attribute;
-use crate::{Error, Identifier, 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
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    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(
-            Identifier::new(variant)?,
-            value.serialize(ExpressionSerializer)?,
-        ))
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeAttributeSeq::new())
-    }
-
-    fn serialize_tuple_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        Ok(SerializeAttributeTupleVariant::new(
-            Identifier::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(
-            Identifier::new(variant)?,
-            len,
-        ))
-    }
-}
-
-pub struct SerializeAttributeSeq {
-    key: Option<Identifier>,
-    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(IdentifierSerializer)?);
-        } 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 {
-    impl_forward_to_serialize_seq!(serialize_element, Attribute);
-}
-
-impl ser::SerializeTupleStruct for SerializeAttributeSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Attribute);
-}
-
-pub struct SerializeAttributeTupleVariant {
-    key: Identifier,
-    vec: Vec<Expression>,
-}
-
-impl SerializeAttributeTupleVariant {
-    pub fn new(key: Identifier, len: usize) -> Self {
-        SerializeAttributeTupleVariant {
-            key,
-            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<Identifier>,
-    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(IdentifierSerializer)?);
-            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,
-    {
-        assert!(
-            self.key.is_some(),
-            "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<Identifier>,
-    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(IdentifierSerializer)?),
-            "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: Identifier,
-    map: Object<ObjectKey, Expression>,
-}
-
-impl SerializeAttributeStructVariant {
-    pub fn new(key: Identifier, len: usize) -> Self {
-        SerializeAttributeStructVariant {
-            key,
-            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 key = Identifier::new(key)?;
-        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
deleted file mode 100644
index 65aaa1c..0000000
--- a/src/structure/ser/block.rs
+++ /dev/null
@@ -1,387 +0,0 @@
-use super::{BodySerializer, StructureSerializer};
-use crate::expr::ser::ExpressionSerializer;
-use crate::ser::{IdentifierSerializer, SeqSerializer};
-use crate::{Attribute, Block, BlockLabel, Body, Error, Identifier, 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
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    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: Identifier::new(variant)?,
-            labels: Vec::new(),
-            body: value.serialize(BodySerializer)?,
-        })
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeBlockSeq::new())
-    }
-
-    fn serialize_tuple_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        Ok(SerializeBlockVariant::new(Identifier::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(Identifier::new(variant)?, len))
-    }
-}
-
-pub struct SerializeBlockSeq {
-    identifier: Option<Identifier>,
-    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(IdentifierSerializer)?);
-        } 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 {
-    impl_forward_to_serialize_seq!(serialize_element, Block);
-}
-
-impl ser::SerializeTupleStruct for SerializeBlockSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Block);
-}
-
-pub struct SerializeBlockVariant {
-    identifier: Identifier,
-    structures: Vec<Structure>,
-}
-
-impl SerializeBlockVariant {
-    pub fn new(identifier: Identifier, len: usize) -> Self {
-        SerializeBlockVariant {
-            identifier,
-            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 key = Identifier::new(key)?;
-        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<Identifier>,
-    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(IdentifierSerializer)?);
-            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,
-    {
-        assert!(
-            self.identifier.is_some(),
-            "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(identifier), Some(body)) => Ok(Block {
-                identifier,
-                labels: Vec::new(),
-                body,
-            }),
-            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
-        }
-    }
-}
-
-pub struct SerializeBlockStruct {
-    identifier: Option<Identifier>,
-    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(IdentifierSerializer)?),
-            "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
-        seq tuple tuple_struct tuple_variant map struct struct_variant
-    }
-    serialize_self! { some newtype_struct }
-
-    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.to_string()))
-    }
-
-    fn serialize_unit_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<Self::Ok> {
-        Identifier::new(variant).map(BlockLabel::Identifier)
-    }
-
-    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)?,
-            )),
-            (_, _) => 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/mod.rs b/src/structure/ser/mod.rs
index 93a0f9d..e4092ce 100644
--- a/src/structure/ser/mod.rs
+++ b/src/structure/ser/mod.rs
@@ -1,27 +1,121 @@
 //! Serializer impls for HCL structure types.
 
-mod attribute;
-mod block;
-mod structure;
 #[cfg(test)]
 mod tests;
 
-use self::{
-    attribute::{
-        AttributeSerializer, SerializeAttributeMap, SerializeAttributeSeq,
-        SerializeAttributeStruct, SerializeAttributeStructVariant, SerializeAttributeTupleVariant,
-    },
-    block::{BlockSerializer, SerializeBlockSeq, SerializeBlockStruct},
-    structure::{
-        SerializeStructureStructVariant, SerializeStructureTupleVariant, StructureSerializer,
-    },
+use super::{Attribute, Block, BlockLabel, Body, Structure};
+use crate::expr::ser::{
+    ExpressionSerializer, SerializeExpressionMap, SerializeExpressionStruct,
+    SerializeExpressionStructVariant, SerializeExpressionTupleVariant,
 };
-use crate::expr::ser::ExpressionSerializer;
-use crate::ser::IdentifierSerializer;
-use crate::{Attribute, Body, Error, Identifier, Result, Structure};
-use serde::ser::{self, Serialize, SerializeMap};
+use crate::ser::{
+    in_internal_serialization, IdentifierSerializer, InternalHandles,
+    SerializeInternalHandleStruct, StringSerializer,
+};
+use crate::{Error, Expression, Identifier, Result};
+use serde::ser::{self, Serialize, SerializeMap, SerializeStruct};
+use std::fmt;
+
+const BLOCK_MARKER: &str = "$hcl::Block";
+const LABELED_BLOCK_MARKER: &str = "$hcl::LabeledBlock";
+
+const STRUCTURE_HANDLE_MARKER: &str = "\x00$hcl::StructureHandle";
+
+thread_local! {
+    static STRUCTURE_HANDLES: InternalHandles<Structure> = InternalHandles::new(STRUCTURE_HANDLE_MARKER);
+}
+
+impl ser::Serialize for Attribute {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        if in_internal_serialization() {
+            STRUCTURE_HANDLES.with(|sh| sh.serialize(self.clone(), serializer))
+        } else {
+            let mut s = serializer.serialize_struct("Attribute", 2)?;
+            s.serialize_field("key", &self.key)?;
+            s.serialize_field("expr", &self.expr)?;
+            s.end()
+        }
+    }
+}
+
+impl ser::Serialize for Block {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        if in_internal_serialization() {
+            STRUCTURE_HANDLES.with(|sh| sh.serialize(self.clone(), serializer))
+        } else {
+            let mut s = serializer.serialize_struct("Block", 3)?;
+            s.serialize_field("identifier", &self.identifier)?;
+            s.serialize_field("labels", &self.labels)?;
+            s.serialize_field("body", &self.body)?;
+            s.end()
+        }
+    }
+}
+
+impl ser::Serialize for Structure {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        if in_internal_serialization() {
+            return STRUCTURE_HANDLES.with(|sh| sh.serialize(self.clone(), serializer));
+        }
+
+        match self {
+            Structure::Attribute(attr) => attr.serialize(serializer),
+            Structure::Block(block) => block.serialize(serializer),
+        }
+    }
+}
+
+pub(crate) enum Structures {
+    Single(Structure),
+    Multiple(Vec<Structure>),
+}
+
+impl From<Structures> for Body {
+    fn from(value: Structures) -> Self {
+        Body::from_iter(value)
+    }
+}
+
+impl From<Attribute> for Structures {
+    fn from(attr: Attribute) -> Self {
+        Structures::Single(Structure::Attribute(attr))
+    }
+}
+
+impl From<Block> for Structures {
+    fn from(block: Block) -> Self {
+        Structures::Single(Structure::Block(block))
+    }
+}
+
+impl From<Vec<Structure>> for Structures {
+    fn from(structures: Vec<Structure>) -> Self {
+        Structures::Multiple(structures)
+    }
+}
 
-pub struct BodySerializer;
+impl IntoIterator for Structures {
+    type Item = Structure;
+    type IntoIter = std::vec::IntoIter<Structure>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        match self {
+            Structures::Single(single) => vec![single].into_iter(),
+            Structures::Multiple(multiple) => multiple.into_iter(),
+        }
+    }
+}
+
+pub(crate) struct BodySerializer;
 
 impl ser::Serializer for BodySerializer {
     type Ok = Body;
@@ -44,20 +138,19 @@ impl ser::Serializer for BodySerializer {
 
     fn serialize_newtype_variant<T>(
         self,
-        name: &'static str,
-        variant_index: u32,
+        _name: &'static str,
+        _variant_index: u32,
         variant: &'static str,
         value: &T,
-    ) -> Result<Body>
+    ) -> Result<Self::Ok>
     where
         T: ?Sized + Serialize,
     {
-        match name {
-            "$hcl::body" => value.serialize(self),
-            _ => StructureSerializer
-                .serialize_newtype_variant(name, variant_index, variant, value)
-                .map(Into::into),
-        }
+        let ident = Identifier::new(variant)?;
+
+        value
+            .serialize(StructureSerializer::new(ident))
+            .map(Into::into)
     }
 
     fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
@@ -99,12 +192,12 @@ impl ser::Serializer for BodySerializer {
     }
 }
 
-pub struct SerializeBodySeq {
+pub(crate) struct SerializeBodySeq {
     vec: Vec<Structure>,
 }
 
 impl SerializeBodySeq {
-    pub fn new(len: Option<usize>) -> Self {
+    fn new(len: Option<usize>) -> Self {
         SerializeBodySeq {
             vec: Vec::with_capacity(len.unwrap_or(0)),
         }
@@ -119,7 +212,7 @@ impl ser::SerializeSeq for SerializeBodySeq {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(value.serialize(StructureSerializer)?);
+        self.vec.extend(value.serialize(BodySerializer)?);
         Ok(())
     }
 
@@ -136,29 +229,44 @@ impl ser::SerializeTupleStruct for SerializeBodySeq {
     impl_forward_to_serialize_seq!(serialize_field, Body);
 }
 
-pub struct SerializeBodyTupleVariant {
-    inner: SerializeStructureTupleVariant,
+pub(crate) struct SerializeBodyTupleVariant {
+    ident: Identifier,
+    elements: Vec<Expression>,
 }
 
 impl SerializeBodyTupleVariant {
-    pub fn new(key: Identifier, len: usize) -> Self {
+    fn new(ident: Identifier, len: usize) -> Self {
         SerializeBodyTupleVariant {
-            inner: SerializeStructureTupleVariant::new(key, len),
+            ident,
+            elements: Vec::with_capacity(len),
         }
     }
 }
 
 impl ser::SerializeTupleVariant for SerializeBodyTupleVariant {
-    impl_forward_to_inner!(Body, serialize_field);
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.elements.push(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.ident, self.elements).into())
+    }
 }
 
-pub struct SerializeBodyMap {
+pub(crate) struct SerializeBodyMap {
     structures: Vec<Structure>,
     next_key: Option<Identifier>,
 }
 
 impl SerializeBodyMap {
-    pub fn new(len: Option<usize>) -> Self {
+    fn new(len: Option<usize>) -> Self {
         SerializeBodyMap {
             structures: Vec::with_capacity(len.unwrap_or(0)),
             next_key: None,
@@ -184,8 +292,9 @@ impl ser::SerializeMap for SerializeBodyMap {
     {
         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());
+
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(key))?);
         Ok(())
     }
 
@@ -194,18 +303,17 @@ impl ser::SerializeMap for SerializeBodyMap {
     }
 }
 
-pub enum SerializeBodyStruct {
-    Attribute(SerializeAttributeStruct),
-    Block(SerializeBlockStruct),
-    Other(SerializeBodyMap),
+pub(crate) enum SerializeBodyStruct {
+    InternalHandle(SerializeInternalHandleStruct),
+    Map(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))),
+    fn new(name: &'static str, len: usize) -> Self {
+        if name == STRUCTURE_HANDLE_MARKER {
+            SerializeBodyStruct::InternalHandle(SerializeInternalHandleStruct::new())
+        } else {
+            SerializeBodyStruct::Map(SerializeBodyMap::new(Some(len)))
         }
     }
 }
@@ -216,36 +324,1073 @@ impl ser::SerializeStruct for SerializeBodyStruct {
 
     fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
     where
-        T: ?Sized + Serialize,
+        T: ?Sized + ser::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),
+            SerializeBodyStruct::Map(ser) => ser.serialize_entry(key, value),
+            SerializeBodyStruct::InternalHandle(ser) => ser.serialize_field(key, value),
         }
     }
 
-    fn end(self) -> Result<Body> {
+    fn end(self) -> Result<Self::Ok> {
         match self {
-            SerializeBodyStruct::Attribute(ser) => ser.end().map(Into::into),
-            SerializeBodyStruct::Block(ser) => ser.end().map(Into::into),
-            SerializeBodyStruct::Other(ser) => ser.end(),
+            SerializeBodyStruct::InternalHandle(ser) => ser
+                .end()
+                .map(|handle| STRUCTURE_HANDLES.with(|sh| sh.remove(handle)).into()),
+            SerializeBodyStruct::Map(ser) => ser.end(),
         }
     }
 }
 
-pub struct SerializeBodyStructVariant {
-    inner: SerializeStructureStructVariant,
+pub(crate) struct SerializeBodyStructVariant {
+    ident: Identifier,
+    structures: Vec<Structure>,
 }
 
 impl SerializeBodyStructVariant {
-    pub fn new(key: Identifier, len: usize) -> Self {
+    fn new(ident: Identifier, len: usize) -> Self {
         SerializeBodyStructVariant {
-            inner: SerializeStructureStructVariant::new(key, len),
+            ident,
+            structures: Vec::with_capacity(len),
         }
     }
 }
 
 impl ser::SerializeStructVariant for SerializeBodyStructVariant {
-    impl_forward_to_inner!(Body, serialize_field(key: &'static str));
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let ident = Identifier::new(key)?;
+
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(ident))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block::builder(self.ident)
+            .add_structures(self.structures)
+            .build()
+            .into())
+    }
+}
+
+pub(crate) struct StructureSerializer {
+    ident: Identifier,
+}
+
+impl StructureSerializer {
+    fn new(ident: Identifier) -> Self {
+        StructureSerializer { ident }
+    }
+
+    fn into_attr(self, expr: Expression) -> Structures {
+        Attribute::new(self.ident, expr).into()
+    }
+}
+
+impl ser::Serializer for StructureSerializer {
+    type Ok = Structures;
+    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_self! { some }
+    forward_to_serialize_seq! { tuple tuple_struct }
+
+    fn serialize_bool(self, value: bool) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_bool(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_i8(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_i16(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_i32(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_i64(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_u8(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_u16(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_u32(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_u64(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_f32(self, value: f32) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_f32(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_f64(self, value: f64) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_f64(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_char(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_str(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_bytes(value)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_unit(self) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_unit()
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_unit_struct(name)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_unit_variant(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_unit_variant(name, variant_index, variant)
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if name == BLOCK_MARKER {
+            BlockSerializer::new(self.ident).serialize_newtype_struct(name, value)
+        } else if name == LABELED_BLOCK_MARKER {
+            LabeledBlockSerializer::new(self.ident).serialize_newtype_struct(name, value)
+        } else {
+            ExpressionSerializer
+                .serialize_newtype_struct(name, value)
+                .map(|expr| self.into_attr(expr))
+        }
+    }
+
+    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,
+    {
+        if name == BLOCK_MARKER {
+            BlockSerializer::new(self.ident).serialize_newtype_variant(
+                name,
+                variant_index,
+                variant,
+                value,
+            )
+        } else if name == LABELED_BLOCK_MARKER {
+            LabeledBlockSerializer::new(self.ident).serialize_newtype_variant(
+                name,
+                variant_index,
+                variant,
+                value,
+            )
+        } else {
+            ExpressionSerializer
+                .serialize_newtype_variant(name, variant_index, variant, value)
+                .map(|expr| self.into_attr(expr))
+        }
+    }
+
+    fn serialize_none(self) -> Result<Self::Ok> {
+        ExpressionSerializer
+            .serialize_none()
+            .map(|expr| self.into_attr(expr))
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeStructureSeq::new(self.ident, len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        SerializeStructureTupleVariant::new(self.ident, name, variant, len)
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeStructureMap::new(self.ident, len))
+    }
+
+    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeStructureStruct::new(self.ident, name, len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        SerializeStructureStructVariant::new(self.ident, name, variant, len)
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + fmt::Display,
+    {
+        ExpressionSerializer
+            .collect_str(value)
+            .map(|expr| self.into_attr(expr))
+    }
+}
+
+pub(crate) struct SerializeStructureSeq {
+    ident: Identifier,
+    elements: Vec<Expression>,
+}
+
+impl SerializeStructureSeq {
+    fn new(ident: Identifier, len: Option<usize>) -> Self {
+        SerializeStructureSeq {
+            ident,
+            elements: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeStructureSeq {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.elements.push(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.ident, self.elements).into())
+    }
+}
+
+impl ser::SerializeTuple for SerializeStructureSeq {
+    impl_forward_to_serialize_seq!(serialize_element, Structures);
+}
+
+impl ser::SerializeTupleStruct for SerializeStructureSeq {
+    impl_forward_to_serialize_seq!(serialize_field, Structures);
+}
+
+pub(crate) enum SerializeStructureTupleVariant {
+    Attribute(Identifier, SerializeExpressionTupleVariant),
+    Block(SerializeBlockTupleVariant),
+    LabeledBlock(SerializeLabeledBlockTupleVariant),
+}
+
+impl SerializeStructureTupleVariant {
+    fn new(
+        ident: Identifier,
+        name: &'static str,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self> {
+        if name == BLOCK_MARKER {
+            Ok(SerializeStructureTupleVariant::Block(
+                SerializeBlockTupleVariant::new(ident, Identifier::new(variant)?, len),
+            ))
+        } else if name == LABELED_BLOCK_MARKER {
+            Ok(SerializeStructureTupleVariant::LabeledBlock(
+                SerializeLabeledBlockTupleVariant::new(ident, variant, len),
+            ))
+        } else {
+            Ok(SerializeStructureTupleVariant::Attribute(
+                ident,
+                SerializeExpressionTupleVariant::new(variant, len),
+            ))
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeStructureTupleVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureTupleVariant::Attribute(_, ser) => ser.serialize_field(value),
+            SerializeStructureTupleVariant::Block(ser) => ser.serialize_field(value),
+            SerializeStructureTupleVariant::LabeledBlock(ser) => ser.serialize_field(value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureTupleVariant::Attribute(ident, ser) => {
+                ser.end().map(|expr| Attribute::new(ident, expr).into())
+            }
+            SerializeStructureTupleVariant::Block(ser) => ser.end(),
+            SerializeStructureTupleVariant::LabeledBlock(ser) => ser.end(),
+        }
+    }
+}
+
+pub(crate) struct SerializeStructureMap {
+    ident: Identifier,
+    inner: SerializeExpressionMap,
+}
+
+impl SerializeStructureMap {
+    fn new(ident: Identifier, len: Option<usize>) -> Self {
+        SerializeStructureMap {
+            ident,
+            inner: SerializeExpressionMap::new(len),
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeStructureMap {
+    type Ok = Structures;
+    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(|expr| Attribute::new(self.ident, expr).into())
+    }
+}
+
+pub(crate) enum SerializeStructureStruct {
+    Attribute(Identifier, SerializeExpressionStruct),
+    Block(SerializeBlockStruct),
+    LabeledBlock(SerializeLabeledBlockStruct),
+}
+
+impl SerializeStructureStruct {
+    fn new(ident: Identifier, name: &'static str, len: usize) -> Self {
+        if name == BLOCK_MARKER {
+            SerializeStructureStruct::Block(SerializeBlockStruct::new(ident, len))
+        } else if name == LABELED_BLOCK_MARKER {
+            SerializeStructureStruct::LabeledBlock(SerializeLabeledBlockStruct::new(ident, len))
+        } else {
+            SerializeStructureStruct::Attribute(ident, SerializeExpressionStruct::new(name, len))
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeStructureStruct {
+    type Ok = Structures;
+    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::LabeledBlock(ser) => ser.serialize_field(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureStruct::Attribute(ident, ser) => {
+                ser.end().map(|expr| Attribute::new(ident, expr).into())
+            }
+            SerializeStructureStruct::Block(ser) => ser.end(),
+            SerializeStructureStruct::LabeledBlock(ser) => ser.end(),
+        }
+    }
+}
+
+pub(crate) enum SerializeStructureStructVariant {
+    Attribute(Identifier, SerializeExpressionStructVariant),
+    Block(SerializeBlockStructVariant),
+    LabeledBlock(SerializeLabeledBlockStructVariant),
+}
+
+impl SerializeStructureStructVariant {
+    fn new(
+        ident: Identifier,
+        name: &'static str,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self> {
+        if name == BLOCK_MARKER {
+            Ok(SerializeStructureStructVariant::Block(
+                SerializeBlockStructVariant::new(ident, Identifier::new(variant)?, len),
+            ))
+        } else if name == LABELED_BLOCK_MARKER {
+            Ok(SerializeStructureStructVariant::LabeledBlock(
+                SerializeLabeledBlockStructVariant::new(ident, variant, len),
+            ))
+        } else {
+            Ok(SerializeStructureStructVariant::Attribute(
+                ident,
+                SerializeExpressionStructVariant::new(variant, len),
+            ))
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeStructureStructVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureStructVariant::Attribute(_, ser) => ser.serialize_field(key, value),
+            SerializeStructureStructVariant::Block(ser) => ser.serialize_field(key, value),
+            SerializeStructureStructVariant::LabeledBlock(ser) => ser.serialize_field(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureStructVariant::Attribute(ident, ser) => {
+                ser.end().map(|expr| Attribute::new(ident, expr).into())
+            }
+            SerializeStructureStructVariant::Block(ser) => ser.end(),
+            SerializeStructureStructVariant::LabeledBlock(ser) => ser.end(),
+        }
+    }
+}
+
+pub(crate) struct BlockSerializer {
+    ident: Identifier,
+}
+
+impl BlockSerializer {
+    fn new(ident: Identifier) -> Self {
+        BlockSerializer { ident }
+    }
+}
+
+impl ser::Serializer for BlockSerializer {
+    type Ok = Structures;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBlockSeq;
+    type SerializeTuple = SerializeBlockSeq;
+    type SerializeTupleStruct = SerializeBlockSeq;
+    type SerializeTupleVariant = SerializeBlockTupleVariant;
+    type SerializeMap = SerializeBlockMap;
+    type SerializeStruct = SerializeBlockStruct;
+    type SerializeStructVariant = SerializeBlockStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+    serialize_self! { some newtype_struct }
+    forward_to_serialize_seq! { tuple tuple_struct }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        let variant = Identifier::new(variant)?;
+
+        Ok(Block::builder(self.ident)
+            .add_structures(value.serialize(StructureSerializer::new(variant))?)
+            .build()
+            .into())
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBlockSeq::new(self.ident, len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeBlockTupleVariant::new(
+            self.ident,
+            Identifier::new(variant)?,
+            len,
+        ))
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeBlockMap::new(self.ident, len))
+    }
+
+    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeBlockStruct::new(self.ident, len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeBlockStructVariant::new(
+            self.ident,
+            Identifier::new(variant)?,
+            len,
+        ))
+    }
+}
+
+pub(crate) struct SerializeBlockSeq {
+    ident: Identifier,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockSeq {
+    fn new(ident: Identifier, len: Option<usize>) -> Self {
+        SerializeBlockSeq {
+            ident,
+            structures: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeBlockSeq {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(self.ident.clone()))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(self.structures.into())
+    }
+}
+
+impl ser::SerializeTuple for SerializeBlockSeq {
+    impl_forward_to_serialize_seq!(serialize_element, Structures);
+}
+
+impl ser::SerializeTupleStruct for SerializeBlockSeq {
+    impl_forward_to_serialize_seq!(serialize_field, Structures);
+}
+
+pub(crate) struct SerializeBlockTupleVariant {
+    ident: Identifier,
+    variant: Identifier,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockTupleVariant {
+    fn new(ident: Identifier, variant: Identifier, len: usize) -> Self {
+        SerializeBlockTupleVariant {
+            ident,
+            variant,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeBlockTupleVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(self.variant.clone()))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block::builder(self.ident)
+            .add_structures(self.structures)
+            .build()
+            .into())
+    }
+}
+
+pub(crate) struct SerializeBlockMap {
+    ident: Identifier,
+    next_key: Option<Identifier>,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockMap {
+    fn new(ident: Identifier, len: Option<usize>) -> Self {
+        SerializeBlockMap {
+            ident,
+            next_key: None,
+            structures: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeBlockMap {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.next_key = Some(key.serialize(IdentifierSerializer)?);
+        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");
+
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(key))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block::builder(self.ident)
+            .add_structures(self.structures)
+            .build()
+            .into())
+    }
+}
+
+pub(crate) struct SerializeBlockStruct {
+    ident: Identifier,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockStruct {
+    fn new(ident: Identifier, len: usize) -> Self {
+        SerializeBlockStruct {
+            ident,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeBlockStruct {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let ident = Identifier::new(key)?;
+
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(ident))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block::builder(self.ident)
+            .add_structures(self.structures)
+            .build()
+            .into())
+    }
+}
+
+pub(crate) struct SerializeBlockStructVariant {
+    ident: Identifier,
+    variant: Identifier,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockStructVariant {
+    fn new(ident: Identifier, variant: Identifier, len: usize) -> Self {
+        SerializeBlockStructVariant {
+            ident,
+            variant,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeBlockStructVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let ident = Identifier::new(key)?;
+
+        self.structures
+            .extend(value.serialize(StructureSerializer::new(ident))?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block::builder(self.ident)
+            .add_block(
+                Block::builder(self.variant)
+                    .add_structures(self.structures)
+                    .build(),
+            )
+            .build()
+            .into())
+    }
+}
+
+pub(crate) struct LabeledBlockSerializer {
+    ident: Identifier,
+}
+
+impl LabeledBlockSerializer {
+    fn new(ident: Identifier) -> Self {
+        LabeledBlockSerializer { ident }
+    }
+}
+
+impl ser::Serializer for LabeledBlockSerializer {
+    type Ok = Structures;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBlockSeq;
+    type SerializeTuple = SerializeBlockSeq;
+    type SerializeTupleStruct = SerializeBlockSeq;
+    type SerializeTupleVariant = SerializeLabeledBlockTupleVariant;
+    type SerializeMap = SerializeLabeledBlockMap;
+    type SerializeStruct = SerializeLabeledBlockStruct;
+    type SerializeStructVariant = SerializeLabeledBlockStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+    serialize_self! { some newtype_struct }
+    forward_to_serialize_seq! { tuple tuple_struct }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        let mut structures = Vec::with_capacity(1);
+
+        serialize_blocks(self.ident, value, &mut structures, |labels| {
+            labels.insert(0, variant.into());
+        })?;
+
+        Ok(structures.into())
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBlockSeq::new(self.ident, len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeLabeledBlockTupleVariant::new(
+            self.ident, variant, len,
+        ))
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeLabeledBlockMap::new(self.ident, len))
+    }
+
+    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeLabeledBlockStruct::new(self.ident, len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeLabeledBlockStructVariant::new(
+            self.ident, variant, len,
+        ))
+    }
+}
+
+pub(crate) struct SerializeLabeledBlockTupleVariant {
+    ident: Identifier,
+    variant: &'static str,
+    structures: Vec<Structure>,
+}
+
+impl SerializeLabeledBlockTupleVariant {
+    fn new(ident: Identifier, variant: &'static str, len: usize) -> Self {
+        SerializeLabeledBlockTupleVariant {
+            ident,
+            variant,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeLabeledBlockTupleVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        serialize_blocks(self.ident.clone(), value, &mut self.structures, |labels| {
+            labels.insert(0, self.variant.into());
+        })
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(self.structures.into())
+    }
+}
+
+pub(crate) struct SerializeLabeledBlockMap {
+    ident: Identifier,
+    next_key: Option<String>,
+    structures: Vec<Structure>,
+}
+
+impl SerializeLabeledBlockMap {
+    fn new(ident: Identifier, len: Option<usize>) -> Self {
+        SerializeLabeledBlockMap {
+            ident,
+            next_key: None,
+            structures: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeLabeledBlockMap {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.next_key = Some(key.serialize(StringSerializer)?);
+        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");
+
+        serialize_blocks(self.ident.clone(), value, &mut self.structures, |labels| {
+            labels.insert(0, BlockLabel::from(&key));
+        })
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(self.structures.into())
+    }
+}
+
+pub(crate) struct SerializeLabeledBlockStruct {
+    ident: Identifier,
+    structures: Vec<Structure>,
+}
+
+impl SerializeLabeledBlockStruct {
+    fn new(ident: Identifier, len: usize) -> Self {
+        SerializeLabeledBlockStruct {
+            ident,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeLabeledBlockStruct {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        serialize_blocks(self.ident.clone(), value, &mut self.structures, |labels| {
+            labels.insert(0, key.into());
+        })
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(self.structures.into())
+    }
+}
+
+pub(crate) struct SerializeLabeledBlockStructVariant {
+    ident: Identifier,
+    variant: &'static str,
+    structures: Vec<Structure>,
+}
+
+impl SerializeLabeledBlockStructVariant {
+    fn new(ident: Identifier, variant: &'static str, len: usize) -> Self {
+        SerializeLabeledBlockStructVariant {
+            ident,
+            variant,
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeLabeledBlockStructVariant {
+    type Ok = Structures;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        serialize_blocks(self.ident.clone(), value, &mut self.structures, |labels| {
+            labels.insert(0, self.variant.into());
+            labels.insert(1, key.into());
+        })
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(self.structures.into())
+    }
+}
+
+fn serialize_blocks<T, F>(
+    ident: Identifier,
+    value: &T,
+    structures: &mut Vec<Structure>,
+    mut f: F,
+) -> Result<()>
+where
+    T: ?Sized + Serialize,
+    F: FnMut(&mut Vec<BlockLabel>),
+{
+    for structure in value.serialize(StructureSerializer::new(ident))? {
+        match structure {
+            Structure::Attribute(attr) => {
+                return Err(ser::Error::custom(format!(
+                    "block expected, found attribute: {:?}",
+                    attr
+                )))
+            }
+            Structure::Block(mut block) => {
+                f(&mut block.labels);
+                structures.push(block.into());
+            }
+        }
+    }
+
+    Ok(())
 }
diff --git a/src/structure/ser/structure.rs b/src/structure/ser/structure.rs
deleted file mode 100644
index 2126db4..0000000
--- a/src/structure/ser/structure.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use super::{
-    AttributeSerializer, BlockSerializer, SerializeAttributeMap, SerializeAttributeSeq,
-    SerializeAttributeStruct, SerializeAttributeStructVariant, SerializeAttributeTupleVariant,
-    SerializeBlockSeq, SerializeBlockStruct,
-};
-use crate::{Error, Identifier, 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
-    }
-    serialize_self! { some newtype_struct }
-    forward_to_serialize_seq! { tuple tuple_struct }
-
-    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_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        Ok(SerializeStructureTupleVariant::new(
-            Identifier::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(
-            Identifier::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 {
-    impl_forward_to_serialize_seq!(serialize_element, Structure);
-}
-
-impl ser::SerializeTupleStruct for SerializeStructureSeq {
-    impl_forward_to_serialize_seq!(serialize_field, Structure);
-}
-
-pub struct SerializeStructureTupleVariant {
-    inner: SerializeAttributeTupleVariant,
-}
-
-impl SerializeStructureTupleVariant {
-    pub fn new(key: Identifier, len: usize) -> Self {
-        SerializeStructureTupleVariant {
-            inner: SerializeAttributeTupleVariant::new(key, len),
-        }
-    }
-}
-
-impl ser::SerializeTupleVariant for SerializeStructureTupleVariant {
-    impl_forward_to_inner!(Structure, serialize_field);
-}
-
-pub struct SerializeStructureMap {
-    inner: SerializeAttributeMap,
-}
-
-impl SerializeStructureMap {
-    fn new() -> Self {
-        SerializeStructureMap {
-            inner: SerializeAttributeMap::new(),
-        }
-    }
-}
-
-impl ser::SerializeMap for SerializeStructureMap {
-    impl_forward_to_inner!(Structure, serialize_key, serialize_value);
-}
-
-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(key: Identifier, len: usize) -> Self {
-        SerializeStructureStructVariant {
-            inner: SerializeAttributeStructVariant::new(key, len),
-        }
-    }
-}
-
-impl ser::SerializeStructVariant for SerializeStructureStructVariant {
-    impl_forward_to_inner!(Structure, serialize_field(key: &'static str));
-}


Test patch:
diff --git a/src/expr/ser/tests.rs b/src/expr/ser/tests.rs
index fa6cdad..7a7a912 100644
--- a/src/expr/ser/tests.rs
+++ b/src/expr/ser/tests.rs
@@ -1,101 +1,38 @@
-use super::{
-    ConditionalSerializer, ExpressionSerializer, ForExprSerializer, OperationSerializer,
-    TemplateExprSerializer,
-};
 use crate::expr::*;
 use crate::Identifier;
 use serde::ser;
-use std::fmt::Debug;
 
 #[track_caller]
-fn test_identity<S, T>(ser: S, value: T)
+fn assert_expr<G>(given: G, expected: Expression)
 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());
+    assert_eq!(Expression::from_serializable(&given).unwrap(), expected);
 }
 
 #[test]
-fn identity() {
-    test_identity(ExpressionSerializer, Expression::Null);
-    test_identity(ExpressionSerializer, Expression::Number(1.into()));
-    test_identity(ExpressionSerializer, Expression::String("bar".into()));
-    test_identity(
-        ExpressionSerializer,
+fn roundtrip() {
+    assert_expr(Expression::Null, Expression::Null);
+    assert_expr(Expression::Number(1.into()), Expression::Number(1.into()));
+    assert_expr(
+        Expression::String("bar".into()),
+        Expression::String("bar".into()),
+    );
+    assert_expr(
+        Expression::from_iter([("foo", "bar")]),
         Expression::from_iter([("foo", "bar")]),
     );
-    test_identity(TemplateExprSerializer, TemplateExpr::from("${foo}"));
-    test_identity(
-        TemplateExprSerializer,
-        TemplateExpr::Heredoc(
-            Heredoc::new(Identifier::unchecked("EOS"), "  ${foo}")
-                .with_strip_mode(HeredocStripMode::Indent),
-        ),
-    );
-    test_identity(
-        TemplateExprSerializer,
-        TemplateExpr::Heredoc(Heredoc::new(Identifier::unchecked("EOS"), "${foo}")),
-    );
-    test_identity(
-        ConditionalSerializer,
-        Conditional::new(Variable::unchecked("some_cond_var"), "yes", "no"),
-    );
-    test_identity(
-        OperationSerializer,
-        Operation::Unary(UnaryOp::new(UnaryOperator::Neg, 1)),
-    );
-    test_identity(
-        OperationSerializer,
-        Operation::Binary(BinaryOp::new(1, BinaryOperator::Plus, 1)),
-    );
-    test_identity(
-        ForExprSerializer,
-        ForExpr::new(
-            Identifier::unchecked("value"),
-            vec![Expression::String(String::from("foo"))],
-            Variable::unchecked("other_value"),
-        )
-        .with_key_var(Identifier::unchecked("index"))
-        .with_cond_expr(Expression::Bool(true)),
-    );
-    test_identity(
-        ForExprSerializer,
-        ForExpr::new(
-            Identifier::unchecked("value"),
-            Expression::Object(Object::from([(
-                ObjectKey::from("k"),
-                Expression::String(String::from("v")),
-            )])),
-            Variable::unchecked("other_value"),
-        )
-        .with_key_var(Identifier::unchecked("index"))
-        .with_key_expr(Variable::unchecked("other_key"))
-        .with_cond_expr(Expression::Bool(true))
-        .with_grouping(true),
-    );
+    assert_expr(
+        Expression::from(Variable::unchecked("var")),
+        Expression::from(Variable::unchecked("var")),
+    )
 }
 
 #[test]
-fn custom() {
-    test_serialize(
-        ExpressionSerializer,
-        Some(1u8),
-        Expression::Number(1u8.into()),
-    );
+fn builtin() {
+    assert_expr(Some(1u8), Expression::Number(1u8.into()));
 
-    test_serialize(
-        ExpressionSerializer,
+    assert_expr(
         Conditional::new(Variable::unchecked("some_cond_var"), "yes", "no"),
         Expression::from(Conditional::new(
             Variable::unchecked("some_cond_var"),
@@ -104,14 +41,12 @@ fn custom() {
         )),
     );
 
-    test_serialize(
-        ExpressionSerializer,
+    assert_expr(
         Operation::Unary(UnaryOp::new(UnaryOperator::Neg, 1)),
         Expression::from(Operation::Unary(UnaryOp::new(UnaryOperator::Neg, 1))),
     );
 
-    test_serialize(
-        ExpressionSerializer,
+    assert_expr(
         TemplateExpr::Heredoc(Heredoc::new(Identifier::unchecked("EOS"), "${foo}")),
         Expression::from(TemplateExpr::Heredoc(Heredoc::new(
             Identifier::unchecked("EOS"),
@@ -119,8 +54,7 @@ fn custom() {
         ))),
     );
 
-    test_serialize(
-        ExpressionSerializer,
+    assert_expr(
         ForExpr::new(
             Identifier::unchecked("value"),
             vec![Expression::String(String::from("foo"))],
@@ -139,8 +73,7 @@ fn custom() {
         ),
     );
 
-    test_serialize(
-        ExpressionSerializer,
+    assert_expr(
         ForExpr::new(
             Identifier::unchecked("value"),
             vec![Expression::String(String::from("foo"))],
@@ -161,29 +94,52 @@ fn custom() {
         ),
     );
 
-    test_serialize(
-        ConditionalSerializer,
-        (
-            Expression::from(Variable::unchecked("some_cond_var")),
-            Expression::String("yes".into()),
-            Expression::String("no".into()),
+    assert_expr(
+        Traversal::builder(Variable::unchecked("some_var"))
+            .index(0)
+            .build(),
+        Expression::from(
+            Traversal::builder(Variable::unchecked("some_var"))
+                .index(0)
+                .build(),
         ),
-        Conditional::new(Variable::unchecked("some_cond_var"), "yes", "no"),
     );
 
-    test_serialize(
-        OperationSerializer,
-        ("-", Expression::Number(1.into())),
+    assert_expr(
+        FuncCall::builder("func").arg(0).build(),
+        Expression::from(FuncCall::builder("func").arg(0).build()),
+    );
+
+    assert_expr(
         Operation::Unary(UnaryOp::new(UnaryOperator::Neg, 1)),
+        Expression::from(Operation::Unary(UnaryOp::new(UnaryOperator::Neg, 1))),
     );
 
-    test_serialize(
-        OperationSerializer,
-        (
-            Expression::Number(1.into()),
-            "+",
-            Expression::Number(1.into()),
-        ),
+    assert_expr(
         Operation::Binary(BinaryOp::new(1, BinaryOperator::Plus, 1)),
+        Expression::from(Operation::Binary(BinaryOp::new(1, BinaryOperator::Plus, 1))),
+    );
+
+    assert_expr(
+        TemplateExpr::from("Hello ${world}!"),
+        Expression::from(TemplateExpr::from("Hello ${world}!")),
+    );
+
+    assert_expr(
+        Variable::unchecked("var"),
+        Expression::from(Variable::unchecked("var")),
     );
+
+    assert_expr(
+        RawExpression::new("raw"),
+        Expression::from(RawExpression::new("raw")),
+    );
+}
+
+#[test]
+fn custom() {
+    assert_expr((), Expression::Null);
+    assert_expr(1, Expression::Number(1.into()));
+    assert_expr("bar", Expression::String("bar".into()));
+    assert_expr(["foo", "bar"], Expression::from_iter(["foo", "bar"]));
 }
diff --git a/src/format/tests.rs b/src/format/tests.rs
index 0aeb5dd..1609a13 100644
--- a/src/format/tests.rs
+++ b/src/format/tests.rs
@@ -155,3 +155,15 @@ fn prefer_ident_keys() {
         "object = {\n  foo = 1\n  bar = 2\n  \"baz qux\" = 3\n}\n",
     );
 }
+
+#[test]
+fn to_interpolated_string() {
+    let binop = BinaryOp::new(1, BinaryOperator::Plus, 1);
+    assert_eq!(super::to_interpolated_string(&binop).unwrap(), "${1 + 1}");
+
+    let expr = Expression::from(FuncCall::builder("add").arg(1).arg(1).build());
+    assert_eq!(
+        super::to_interpolated_string(&expr).unwrap(),
+        "${add(1, 1)}"
+    );
+}
diff --git a/src/ser/tests.rs b/src/ser/tests.rs
index 15de4f9..e4047f6 100644
--- a/src/ser/tests.rs
+++ b/src/ser/tests.rs
@@ -405,7 +405,7 @@ qux {
         .dense(true)
         .build(&mut buf);
     let mut ser = Serializer::with_formatter(formatter);
-    body.serialize(&mut ser).unwrap();
+    ser.serialize(&body).unwrap();
 
     assert_eq!(String::from_utf8(buf).unwrap(), custom_expected);
 }
diff --git a/src/structure/ser/tests.rs b/src/structure/ser/tests.rs
index be83fa5..209ad58 100644
--- a/src/structure/ser/tests.rs
+++ b/src/structure/ser/tests.rs
@@ -1,138 +1,198 @@
-use super::{block::BlockLabelSerializer, AttributeSerializer, BlockSerializer, BodySerializer};
-use crate::structure::{Attribute, Block, BlockLabel, Body};
-use crate::{Identifier, Map};
+use crate::expr::{Expression, Variable};
+use crate::structure::{Attribute, Block, Body, Structure};
+use crate::Identifier;
+use indexmap::{indexmap, IndexMap};
+use pretty_assertions::assert_eq;
 use serde::{ser, Serialize};
-use std::fmt::Debug;
 
 #[track_caller]
-fn test_identity<S, T>(ser: S, value: T)
+fn assert_body<T>(given: T, expected: Body)
 where
-    S: ser::Serializer<Ok = T>,
-    T: ser::Serialize + PartialEq + Debug,
+    T: ser::Serialize,
 {
-    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());
+    assert_eq!(Body::from_serializable(&given).unwrap(), expected);
 }
 
 #[test]
-fn identity() {
-    test_identity(BodySerializer, Body::default());
-    test_identity(
-        BodySerializer,
+fn roundtrip() {
+    assert_body(Body::default(), Body::default());
+    assert_body(
+        Body::builder()
+            .add_attribute(("foo", "bar"))
+            .add_block(Block::builder("baz").build())
+            .build(),
         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,
+    assert_body(
+        Structure::Block(
+            Block::builder("foo")
+                .add_label("bar")
+                .add_label(Identifier::unchecked("bar"))
+                .add_attribute(("baz", "qux"))
+                .build(),
+        ),
         Block::builder("foo")
             .add_label("bar")
+            .add_label(Identifier::unchecked("bar"))
             .add_attribute(("baz", "qux"))
-            .build(),
-    );
-    test_identity(BlockLabelSerializer, BlockLabel::from("foo"));
-    test_identity(
-        BlockLabelSerializer,
-        BlockLabel::from(Identifier::unchecked("foo")),
+            .build()
+            .into(),
     );
 }
 
 #[test]
-fn custom() {
-    #[derive(Serialize)]
-    struct CustomAttr {
-        key: &'static str,
-        #[serde(rename = "expr")]
-        value: &'static str,
-    }
-    test_serialize(
-        AttributeSerializer,
-        CustomAttr {
-            key: "foo",
-            value: "bar",
-        },
+fn builtin() {
+    assert_body(
         Attribute::new("foo", "bar"),
+        Attribute::new("foo", "bar").into(),
     );
-    test_serialize(
-        AttributeSerializer,
-        ("foo", "bar"),
-        Attribute::new("foo", "bar"),
+    assert_body(
+        Attribute::new("foo", vec!["bar", "baz"]),
+        Attribute::new("foo", vec!["bar", "baz"]).into(),
     );
-
-    test_serialize(
-        BlockSerializer,
-        {
-            let mut map = Map::new();
-            map.insert("foo", (("bar", "baz"), ("qux", "foo")));
-            map
-        },
+    assert_body(
         Block::builder("foo")
-            .add_attribute(("bar", "baz"))
-            .add_attribute(("qux", "foo"))
+            .add_label("bar")
+            .add_label(Identifier::unchecked("bar"))
+            .add_attribute(("baz", "qux"))
             .build(),
+        Block::builder("foo")
+            .add_label("bar")
+            .add_label(Identifier::unchecked("bar"))
+            .add_attribute(("baz", "qux"))
+            .build()
+            .into(),
     );
+}
+
+#[test]
+fn custom_blocks() {
+    #[derive(Serialize)]
+    #[serde(rename = "$hcl::Block")]
+    struct Unlabeled<T>(T);
+
+    #[derive(Serialize)]
+    #[serde(rename = "$hcl::LabeledBlock")]
+    struct Labeled<T>(T);
 
     #[derive(Serialize)]
-    struct CustomBlock {
-        #[serde(rename = "identifier")]
-        ident: &'static str,
-        body: Map<&'static str, &'static str>,
+    struct A {
+        a: u8,
     }
 
-    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 B {
+        b: u8,
+    }
 
     #[derive(Serialize)]
-    struct CustomLabeledBlock {
-        #[serde(rename = "identifier")]
-        ident: &'static str,
-        labels: [&'static str; 2],
-        body: Map<&'static str, &'static str>,
+    struct C<A, B> {
+        a: A,
+        b: B,
     }
 
-    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(),
-    );
+    #[derive(Serialize)]
+    struct D<T> {
+        d: T,
+    }
+
+    #[derive(Serialize)]
+    struct Config {
+        attr: Expression,
+        unlabeled: Unlabeled<A>,
+        nested: Labeled<C<Labeled<C<Unlabeled<A>, Unlabeled<D<Expression>>>>, Unlabeled<B>>>,
+        nested_many: Labeled<D<Labeled<Vec<Unlabeled<A>>>>>,
+        map: Labeled<IndexMap<&'static str, Labeled<IndexMap<&'static str, Unlabeled<A>>>>>,
+    }
+
+    let given = Config {
+        attr: Expression::Variable(Variable::unchecked("var")),
+        unlabeled: Unlabeled(A { a: 1 }),
+        nested: Labeled(C {
+            a: Labeled(C {
+                a: Unlabeled(A { a: 2 }),
+                b: Unlabeled(D {
+                    d: Expression::Null,
+                }),
+            }),
+            b: Unlabeled(B { b: 2 }),
+        }),
+        nested_many: Labeled(D {
+            d: Labeled(vec![Unlabeled(A { a: 3 }), Unlabeled(A { a: 4 })]),
+        }),
+        map: Labeled(indexmap! {
+            "a" => Labeled(indexmap! {
+                "a" => Unlabeled(A { a: 5 }),
+                "b" => Unlabeled(A { a: 6 }),
+            }),
+            "b" => Labeled(indexmap! {
+                "a" => Unlabeled(A { a: 7 }),
+                "b" => Unlabeled(A { a: 8 }),
+            }),
+        }),
+    };
+
+    let expected = Body::builder()
+        .add_attribute(Attribute::new("attr", Variable::unchecked("var")))
+        .add_block(Block::builder("unlabeled").add_attribute(("a", 1)).build())
+        .add_block(
+            Block::builder("nested")
+                .add_labels(["a", "a"])
+                .add_attribute(("a", 2))
+                .build(),
+        )
+        .add_block(
+            Block::builder("nested")
+                .add_labels(["a", "b"])
+                .add_attribute(("d", Expression::Null))
+                .build(),
+        )
+        .add_block(
+            Block::builder("nested")
+                .add_label("b")
+                .add_attribute(("b", 2))
+                .build(),
+        )
+        .add_block(
+            Block::builder("nested_many")
+                .add_label("d")
+                .add_attribute(("a", 3))
+                .build(),
+        )
+        .add_block(
+            Block::builder("nested_many")
+                .add_label("d")
+                .add_attribute(("a", 4))
+                .build(),
+        )
+        .add_block(
+            Block::builder("map")
+                .add_labels(["a", "a"])
+                .add_attribute(("a", 5))
+                .build(),
+        )
+        .add_block(
+            Block::builder("map")
+                .add_labels(["a", "b"])
+                .add_attribute(("a", 6))
+                .build(),
+        )
+        .add_block(
+            Block::builder("map")
+                .add_labels(["b", "a"])
+                .add_attribute(("a", 7))
+                .build(),
+        )
+        .add_block(
+            Block::builder("map")
+                .add_labels(["b", "b"])
+                .add_attribute(("a", 8))
+                .build(),
+        )
+        .build();
+
+    assert_body(given, expected);
 }