diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -758,6 +758,43 @@
 	* common.mk: Add a file to include with Makefile fragments
 	useful for scanner invocation.
 
+2008-10-13  Andreas Rottmann  <a.rottmann@gmx.at>
+
+	* tests/scanner/utility.h (UtilityTaggedValue): Make the union
+	member anonymous.
+	(UtilityByte): New union typedef with an unnamed struct in it.
+
+	* giscanner/transformer.py (Transformer._create_struct): Create
+	unnamed structs for symbols with a None ident.
+	(Transformer._create_union): Likewise.
+
+	* giscanner/girwriter.py (GIRWriter._write_record): Allow name
+	being None.
+	(GIRWriter._write_union): Likewise.
+
+	* girepository/girparser.c (start_struct): Allow a NULL name for
+	non-toplevel structs.
+	(start_union): Likewise.
+
+	* tests/scanner/utility.h (UtilityTaggedValue): New struct
+	typedef, which has a nested union member.
+	* tests/scanner/utility-expected.gir: Adapted.
+
+	* giscanner/transformer.py (Transformer._create_member): Create
+	struct/union members if appropriate.
+	(Transformer._create_struct, Transformer._create_union): Allow for
+	structs/unions without a C type.
+
+	* giscanner/glibtransformer.py (GLibTransformer._resolve_field):
+	We don't need to resolve non-typef'd
+	(GLibTransformer._resolve_field): Add cases for non-typedef'd
+	struct/union "fields".
+
+	* giscanner/girwriter.py (GIRWriter._write_record): Allow for
+	records without a C type.
+	(GIRWriter._write_field): structs and unions may appear in places
+	where fields do.
+
 2008-11-13  Andreas Rottmann  <a.rottmann@gmx.at>
 	
 	* girepository/girparser.c (ParseContext): Removed member
diff --git a/girepository/girparser.c b/girepository/girparser.c
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -2112,7 +2112,10 @@
 	      GError             **error)
 {
   if (strcmp (element_name, "record") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
@@ -2127,7 +2130,7 @@
       gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values);
       gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values);
 
-      if (name == NULL)
+      if (name == NULL && ctx->node_stack == NULL)
 	{
 	  MISSING_ATTRIBUTE (context, error, element_name, "name");
 	  return FALSE;
@@ -2145,7 +2148,7 @@
 
       struct_ = (GIrNodeStruct *) g_ir_node_new (G_IR_NODE_STRUCT);
       
-      ((GIrNode *)struct_)->name = g_strdup (name);
+      ((GIrNode *)struct_)->name = g_strdup (name ? name : "");
       if (deprecated)
 	struct_->deprecated = TRUE;
       else
@@ -2156,9 +2159,10 @@
 
       struct_->gtype_name = g_strdup (gtype_name);
       struct_->gtype_init = g_strdup (gtype_init);
-      
-      ctx->current_module->entries = 
-	g_list_append (ctx->current_module->entries, struct_);
+
+      if (ctx->node_stack == NULL)
+        ctx->current_module->entries = 
+          g_list_append (ctx->current_module->entries, struct_);
       push_node (ctx, (GIrNode *)struct_);
       
       state_switch (ctx, STATE_STRUCT);
@@ -2176,8 +2180,11 @@
 	     ParseContext       *ctx,
 	     GError             **error)
 {
-  if (strcmp (element_name, "union") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+  if (strcmp (element_name, "union") == 0 &&
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
@@ -2189,7 +2196,7 @@
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
       
-      if (name == NULL)
+      if (name == NULL && ctx->node_stack == NULL)
 	MISSING_ATTRIBUTE (context, error, element_name, "name");
       else
 	{
@@ -2197,7 +2204,7 @@
 
 	  union_ = (GIrNodeUnion *) g_ir_node_new (G_IR_NODE_UNION);
 	  
-	  ((GIrNode *)union_)->name = g_strdup (name);
+	  ((GIrNode *)union_)->name = g_strdup (name ? name : "");
 	  union_->gtype_name = g_strdup (typename);
 	  union_->gtype_init = g_strdup (typeinit);
 	  if (deprecated)
@@ -2205,8 +2212,9 @@
 	  else
 	    union_->deprecated = FALSE;
 
-	  ctx->current_module->entries = 
-	    g_list_append (ctx->current_module->entries, union_);
+          if (ctx->node_stack == NULL)
+            ctx->current_module->entries = 
+              g_list_append (ctx->current_module->entries, union_);
 	  push_node (ctx, (GIrNode *)union_);
 	  
 	  state_switch (ctx, STATE_UNION);
@@ -2685,6 +2693,41 @@
 }
 
 static gboolean
+state_switch_end_struct_or_union (GMarkupParseContext *context,
+                                  ParseContext *ctx,
+                                  const gchar *element_name,
+                                  GError **error)
+{
+  pop_node (ctx);
+  if (ctx->node_stack == NULL)
+    {
+      state_switch (ctx, STATE_NAMESPACE);
+    }
+  else 
+    {
+      if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT)
+        state_switch (ctx, STATE_STRUCT);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION)
+        state_switch (ctx, STATE_UNION);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT)
+        state_switch (ctx, STATE_CLASS);
+      else
+        {
+          int line_number, char_number;
+          g_markup_parse_context_get_position (context, &line_number, &char_number);
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_INVALID_CONTENT,
+                       "Unexpected end tag '%s' on line %d char %d",
+                       element_name,
+                       line_number, char_number);
+          return FALSE;
+        }
+    }
+  return TRUE;
+}
+
+static gboolean
 require_end_element (GMarkupParseContext *context,
 		     ParseContext        *ctx,
 		     const char          *expected_name,
@@ -2897,8 +2940,7 @@
     case STATE_STRUCT:
       if (require_end_element (context, ctx, "record", element_name, error))
 	{
-	  pop_node (ctx);
-	  state_switch (ctx, STATE_NAMESPACE);
+	  state_switch_end_struct_or_union (context, ctx, element_name, error);
 	}
       break;
 
@@ -2914,8 +2956,7 @@
     case STATE_UNION:
       if (require_end_element (context, ctx, "union", element_name, error))
 	{
-	  pop_node (ctx);
-	  state_switch (ctx, STATE_NAMESPACE);
+	  state_switch_end_struct_or_union (context, ctx, element_name, error);
 	}
       break;
     case STATE_IMPLEMENTS:
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -366,8 +366,11 @@
                 ('glib:get-type', boxed.get_type)]
 
     def _write_record(self, record):
-        attrs = [('name', record.name),
-                 ('c:type', record.symbol)]
+        attrs = []
+        if record.name is not None:
+            attrs.append(('name', record.name))
+        if record.symbol is not None: # the record might be anonymous
+            attrs.append(('c:type', record.symbol))
         if record.disguised:
             attrs.append(('disguised', '1'))
         if record.doc:
@@ -386,8 +389,11 @@
                 self._write_method(method)
 
     def _write_union(self, union):
-        attrs = [('name', union.name),
-                 ('c:type', union.symbol)]
+        attrs = []
+        if union.name is not None:
+            attrs.append(('name', union.name))
+        if union.symbol is not None: # the union might be anonymous
+            attrs.append(('c:type', union.symbol))
         if union.doc:
             attrs.append(('doc', union.doc))
         self._append_version(union, attrs)
@@ -410,19 +416,22 @@
 
         if isinstance(field, Callback):
             self._write_callback(field)
-            return
-
-        attrs = [('name', field.name)]
-        # Fields are assumed to be read-only
-        # (see also girparser.c and generate.c)
-        if not field.readable:
-            attrs.append(('readable', '0'))
-        if field.writable:
-            attrs.append(('writable', '1'))
-        if field.bits:
-            attrs.append(('bits', str(field.bits)))
-        with self.tagcontext('field', attrs):
-            self._write_type(field.type)
+        elif isinstance(field, Struct):
+            self._write_record(field)
+        elif isinstance(field, Union):
+            self._write_union(field)
+        else:
+            attrs = [('name', field.name)]
+            # Fields are assumed to be read-only
+            # (see also girparser.c and generate.c)
+            if not field.readable:
+                attrs.append(('readable', '0'))
+            if field.writable:
+                attrs.append(('writable', '1'))
+            if field.bits:
+                attrs.append(('bits', str(field.bits)))
+            with self.tagcontext('field', attrs):
+                self._write_type(field.type)
 
     def _write_signal(self, signal):
         attrs = [('name', signal.name)]
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -854,8 +854,12 @@
     def _resolve_field(self, field):
         if isinstance(field, Callback):
             self._resolve_function(field)
-            return
-        field.type = self._resolve_param_type(field.type)
+        elif isinstance(field, Record): # non-typedef'd struct
+            self._resolve_record(field)
+        elif isinstance(field, Union): # non-typedef'd union
+            self._resolve_union(field)
+        else:
+            field.type = self._resolve_param_type(field.type)
 
     def _resolve_alias(self, alias):
         alias.target = self._resolve_type_name(alias.target, alias.target)
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -307,6 +307,10 @@
         if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
             node = self._create_callback(symbol)
+        elif source_type.type == CTYPE_STRUCT and source_type.name is None:
+            node = self._create_struct(symbol, anonymous=True)
+        elif source_type.type == CTYPE_UNION and source_type.name is None:
+            node = self._create_union(symbol, anonymous=True)
         else:
             # Special handling for fields; we don't have annotations on them
             # to apply later, yet.
@@ -478,8 +482,14 @@
         self._typedefs_ns[callback.name] = callback
         return callback
 
-    def _create_compound(self, klass, symbol):
-        compound = self._typedefs_ns.get(symbol.ident, None)
+    def _create_compound(self, klass, symbol, anonymous):
+        if symbol.ident is None:
+            # the compound is an anonymous member of another union or a struct
+            assert anonymous
+            compound = klass(None, None)
+        else:
+            compound = self._typedefs_ns.get(symbol.ident, None)
+
         if compound is None:
             # This is a bit of a hack; really we should try
             # to resolve through the typedefs to find the real
@@ -500,11 +510,11 @@
 
         return compound
 
-    def _create_struct(self, symbol):
-        return self._create_compound(Struct, symbol)
+    def _create_struct(self, symbol, anonymous=False):
+        return self._create_compound(Struct, symbol, anonymous)
 
-    def _create_union(self, symbol):
-        return self._create_compound(Union, symbol)
+    def _create_union(self, symbol, anonymous=False):
+        return self._create_compound(Union, symbol, anonymous)
 
     def _create_callback(self, symbol):
         parameters = self._create_parameters(symbol.base_type.base_type)
diff --git a/tests/repository/Makefile.am b/tests/repository/Makefile.am
--- a/tests/repository/Makefile.am
+++ b/tests/repository/Makefile.am
@@ -13,4 +13,5 @@
 gitestthrows_LDADD = $(top_builddir)/girepository/libgirepository-1.0.la $(GIREPO_LIBS)
 
 TESTS = gitestrepo gitestthrows
-TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG)
+TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG) \
+   XDG_DATA_DIRS="$(top_srcdir)/gir:$(XDG_DATA_DIRS)"
diff --git a/tests/scanner/utility-1.0-expected.gir b/tests/scanner/utility-1.0-expected.gir
--- a/tests/scanner/utility-1.0-expected.gir
+++ b/tests/scanner/utility-1.0-expected.gir
@@ -46,6 +46,35 @@
         <type name="GObject.ObjectClass" c:type="GObjectClass"/>
       </field>
     </record>
+    <record name="TaggedValue" c:type="UtilityTaggedValue">
+      <field name="tag" writable="1">
+        <type name="int" c:type="int"/>
+      </field>
+      <union>
+        <field name="v_pointer" writable="1">
+          <type name="any" c:type="gpointer"/>
+        </field>
+        <field name="v_real" writable="1">
+          <type name="double" c:type="double"/>
+        </field>
+        <field name="v_integer" writable="1">
+          <type name="long" c:type="long"/>
+        </field>
+      </union>
+    </record>
+    <union name="Byte" c:type="UtilityByte">
+      <field name="value" writable="1">
+        <type name="uint8" c:type="guint8"/>
+      </field>
+      <record>
+        <field name="first_nibble" writable="1" bits="4">
+          <type name="uint8" c:type="guint8"/>
+        </field>
+        <field name="second_nibble" writable="1" bits="4">
+          <type name="uint8" c:type="guint8"/>
+        </field>
+      </record>
+    </union>
     <callback name="FileFunc" c:type="UtilityFileFunc">
       <return-value transfer-ownership="none">
         <type name="none" c:type="void"/>
diff --git a/tests/scanner/utility-1.0-expected.tgir b/tests/scanner/utility-1.0-expected.tgir
--- a/tests/scanner/utility-1.0-expected.tgir
+++ b/tests/scanner/utility-1.0-expected.tgir
@@ -35,6 +35,18 @@
         <type name="GObject.ObjectClass"/>
       </field>
     </record>
+    <record name="TaggedValue">
+      <field name="tag" writable="1">
+        <type name="int"/>
+      </field>
+      <!-- FIXME: anonymous union member missing -->
+    </record>
+    <union name="Byte">
+      <field name="value" writable="1">
+        <type name="uint8"/>
+      </field>
+      <!-- FIXME: anonymous struct member missing -->
+    </union>
     <callback name="FileFunc">
       <return-value transfer-ownership="none">
         <type name="none"/>
diff --git a/tests/scanner/utility.h b/tests/scanner/utility.h
--- a/tests/scanner/utility.h
+++ b/tests/scanner/utility.h
@@ -23,6 +23,27 @@
 /* This one is similar to Pango.Glyph */
 typedef guint32 UtilityGlyph;
 
+typedef struct
+{
+  int tag;
+  union
+  {
+    gpointer v_pointer;
+    double v_real;
+    long v_integer;
+  };
+} UtilityTaggedValue;
+
+typedef union
+{
+  guint8 value;
+  struct
+  {
+    guint8 first_nibble : 4;
+    guint8 second_nibble : 4;
+  };
+} UtilityByte;
+
 typedef void (*UtilityFileFunc)(const char *path, gpointer user_data);
 
 GType                 utility_object_get_type          (void) G_GNUC_CONST;
