public ObjectNode serialize(Model model) {
        ShapeSerializer shapeSerializer = new ShapeSerializer();

        if (version.equals("1.0")) {
            model = ModelTransformer.create().downgradeToV1(model);
        }

        ObjectNode.Builder builder = Node.objectNodeBuilder()
                .withMember("smithy", Node.from(version))
                .withOptionalMember("metadata", createMetadata(model).map(Node::withDeepSortedKeys));

        // Sort shapes by ID.
        Map<StringNode, Node> shapes = new TreeMap<>();
        for (Shape shape : model.toSet()) {
            // Members are serialized inside of other shapes, so filter them out.
            if (!shape.isMemberShape() && shapeFilter.test(shape)) {
                Node value = shape.accept(shapeSerializer);
                shapes.put(Node.from(shape.getId().toString()), value);
                // Add any necessary apply statements to inherited mixin members that added traits, but only if there
                // are actually traits to serialize. Apply statements are used here instead of redefining members on
                // structures because apply statements are more resilient to change over time if the shapes targeted by
                // an inherited member changes.
                if (!shapeSerializer.mixinMemberTraits.isEmpty()) {
                    for (MemberShape member : shapeSerializer.mixinMemberTraits) {
                        Map<StringNode, Node> introducedTraits = createIntroducedTraitsMap(
                                member.getIntroducedTraits().values());
                        if (!introducedTraits.isEmpty()) {
                            ObjectNode.Builder applyBuilder = Node.objectNodeBuilder();
                            applyBuilder.withMember("type", "apply");
                            ObjectNode traits = serializeTraits(applyBuilder, introducedTraits).build();
                            shapes.put(Node.from(member.getId().toString()), traits);
                        }
                    }
                }
            }
        }

        builder.withMember("shapes", new ObjectNode(shapes, SourceLocation.NONE));

        return builder.build();
    }
Serializes a {@link Model} to an {@link ObjectNode}.

<p>The serialized value sorts all map key-value pairs so that they contain
a consistent key ordering, reducing noise in diffs based on
serialized model.

<p>After serializing to an ObjectNode, the node can then be serialized
to formats like JSON, YAML, Ion, etc.
smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java