diff --git a/aas_core_codegen/golang/lib/_generate_verification.py b/aas_core_codegen/golang/lib/_generate_verification.py
index f78d9d4d1..d08bef62d 100644
--- a/aas_core_codegen/golang/lib/_generate_verification.py
+++ b/aas_core_codegen/golang/lib/_generate_verification.py
@@ -116,16 +116,7 @@ class RegexRenderer(parse_retree.Renderer):
def char_to_str_and_escape_or_encode_if_necessary(
self, node: parse_retree.Char, escaping: Mapping[str, str]
) -> List[Union[str, parse_tree.FormattedValue]]:
- if not node.explicitly_encoded:
- escaped = escaping.get(node.character, None)
- if escaped is not None:
- result: List[Union[str, parse_tree.FormattedValue]] = [escaped]
- else:
- result = [node.character]
-
- return result
-
- return [node.character]
+ return [escaping.get(node.character, node.character)]
_REGEX_RENDERER = RegexRenderer()
diff --git a/aas_core_codegen/golang/lib/_generate_xml_common.py b/aas_core_codegen/golang/lib/_generate_xml_common.py
index c2c31679b..08015819c 100644
--- a/aas_core_codegen/golang/lib/_generate_xml_common.py
+++ b/aas_core_codegen/golang/lib/_generate_xml_common.py
@@ -945,6 +945,11 @@ def _generate_read_element_dispatched() -> Stripped:
{II}return
{I}}}
+{I}current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+{I}if err != nil {{
+{II}return
+{I}}}
+
{I}err = checkEndElementInNamespace(current, local, ns)
{I}if err != nil {{
{II}return
diff --git a/aas_core_codegen/golang/main.py b/aas_core_codegen/golang/main.py
index b3884dd36..0c0377965 100644
--- a/aas_core_codegen/golang/main.py
+++ b/aas_core_codegen/golang/main.py
@@ -365,7 +365,7 @@ def execute(context: run.Context, stdout: TextIO, stderr: TextIO) -> int:
if errors is not None:
run.write_error_report(
- message=f"Failed to generate {rel_path} "
+ message=f"Failed to generate {rel_path.as_posix()} "
f"based on {context.model_path}",
errors=[
context.lineno_columner.error_message(error) for error in errors
diff --git a/aas_core_codegen/golang/tests/_generate_xmlization_test_common_test.py b/aas_core_codegen/golang/tests/_generate_xmlization_test_common_test.py
index 58fdc1750..1732be045 100644
--- a/aas_core_codegen/golang/tests/_generate_xmlization_test_common_test.py
+++ b/aas_core_codegen/golang/tests/_generate_xmlization_test_common_test.py
@@ -35,13 +35,14 @@ def generate(repo_url: Stripped) -> str:
Stripped(
f"""\
import (
+{I}"encoding/xml"
{I}"fmt"
{I}"os"
{I}"path/filepath"
-{I}"regexp"
{I}"strings"
{I}"testing"
{I}ourtesting "{repo_url}/ourtesting"
+{I}ourtypes "{repo_url}/types"
{I}ourxmlization "{repo_url}/xmlization"
)"""
),
@@ -86,166 +87,29 @@ def generate(repo_url: Stripped) -> str:
{II}return
{I}}}
{I}return
-}}"""
- ),
- Stripped(
- """\
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174"""
- ),
- Stripped(
- """\
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\\w+(?:\\s[^>]*[^/>])?)>\\w+>`)"""
- ),
- Stripped(
- f"""\
-func forceSelfClosingTags(text string) string {{
-{I}b := []byte(text)
-{I}emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
-{I}if len(emptyTagIdxs) == 0 {{
-{II}return text
-{I}}}
-
-{I}// NOTE (mristin):
-{I}// The matches are walked in order, and the pieces of `b` between them are
-{I}// copied over as they are. Mind that `nb` starts out empty and that `b` is
-{I}// only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
-{I}// writes into `b`'s own backing array, so a document with more than one
-{I}// empty tag came out garbled -- every turn of the loop then indexed into
-{I}// a `b` which the previous turn had already overwritten.
-{I}var nb []byte
-{I}previous := 0
-
-{I}for _, idx := range emptyTagIdxs {{
-{II}// Copy everything since the previous match, then the tag itself --
-{II}// its name and its attributes -- as a self-closing one.
-{II}nb = append(nb, b[previous:idx[0]]...)
-{II}nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
-{II}previous = idx[1]
-{I}}}
-
-{I}// Finally, copy everything after the last match.
-{I}nb = append(nb, b[previous:]...)
-
-{I}return string(nb)
-}}"""
- ),
- Stripped(
- """\
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\\s+<`)"""
- ),
- Stripped(
- f"""\
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {{
-{I}return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}}"""
- ),
- Stripped(
- """\
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\\s+`)"""
- ),
- Stripped(
- f"""\
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {{
-{I}result := text
-{I}result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
-{I}result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
-{I}result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
-{I}return result
}}"""
),
Stripped(
f"""\
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
{I}t *testing.T,
-{I}that string,
+{I}that ourtypes.IClass,
{I}other string,
{I}source string,
) (ok bool) {{
-{I}ok = true
-
-{I}// Remove carriers to avoid problems between Windows, Posix and MacOS
-{I}canonicalThat := strings.ReplaceAll(that, "\\r", "")
-{I}canonicalOther := strings.ReplaceAll(other, "\\r", "")
-
-{I}canonicalThat = strings.TrimSpace(canonicalThat)
-{I}canonicalOther = strings.TrimSpace(canonicalOther)
-
-{I}canonicalThat = forceSelfClosingTags(canonicalThat)
-{I}canonicalOther = forceSelfClosingTags(canonicalOther)
-
-{I}// NOTE (mristin):
-{I}// The following hack is SUPER ugly and unsafe! However, it works. Given Go's
-{I}// limited support for XML, we gave up on a safer approach :(. We tested
-{I}// the following approaches before applying this hack:
-{I}// * A round-trip over `encoding/xml`. Failed due to
-{I}// https://github.com/golang/go/issues/13400.
-{I}// * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
-{I}// element content still has not been de-escaped or consistently escaped in
-{I}// a round trip.
-
-{I}canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
-{I}canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
-{I}canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
-{I}canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
-{I}canonicalThat = removeWhitespaceWithinTags(canonicalThat)
-{I}canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
-{I}thatLines := strings.Split(canonicalThat, "\\n")
-{I}otherLines := strings.Split(canonicalOther, "\\n")
-
-{I}if canonicalThat != canonicalOther {{
-{II}b := new(strings.Builder)
-{II}minLines := len(thatLines)
-{II}if minLines > len(otherLines) {{
-{III}minLines = len(otherLines)
-{II}}}
-{II}for i := 0; i < minLines; i++ {{
-{III}if thatLines[i] == otherLines[i] {{
-{IIII}b.WriteString(fmt.Sprintf(" %s\\n", thatLines[i]))
-{III}}} else {{
-{IIII}b.WriteString(fmt.Sprintf("ORIGINAL %s\\n", thatLines[i]))
-{IIII}b.WriteString(fmt.Sprintf("SERIALIZED %s\\n", otherLines[i]))
-{IIII}break
-{III}}}
-{II}}}
+{I}decoder := xml.NewDecoder(strings.NewReader(other))
+{I}roundTripped, err := ourxmlization.Unmarshal(decoder)
+{I}if !assertNoDeserializationError(t, err, source) {{
+{II}return false
+{I}}}
-{II}ok = false
-{II}t.Fatalf(
-{III}"The canonicalized XML serialization of the de-serialized instance "+
-{IIII}"from %s does not equal the canonicalized original XML document:\\n"+
-{IIII}"%s",
-{III}source, b.String(),
-{II})
-{II}return
+{I}if !ourtesting.DeepEqual(that, roundTripped) {{
+{II}t.Fatalf("XML round-trip changed the instance from %s", source)
+{II}return false
{I}}}
-{I}return
+{I}return true
}}"""
),
Stripped(
diff --git a/aas_core_codegen/golang/tests/_generate_xmlization_test_concrete_classes_test.py b/aas_core_codegen/golang/tests/_generate_xmlization_test_concrete_classes_test.py
index 157928e83..b5018bf47 100644
--- a/aas_core_codegen/golang/tests/_generate_xmlization_test_concrete_classes_test.py
+++ b/aas_core_codegen/golang/tests/_generate_xmlization_test_concrete_classes_test.py
@@ -90,7 +90,7 @@ def _generate_for_cls(cls: intermediate.ConcreteClass) -> List[Stripped]:
{II}ok = assertSerializationEqualsDeserialization(
{III}t,
-{III}text,
+{III}deserialized,
{III}roundTrip,
{III}pth,
{II})
diff --git a/aas_core_codegen/golang/transpilation.py b/aas_core_codegen/golang/transpilation.py
index 99614f58a..ff0a85e45 100644
--- a/aas_core_codegen/golang/transpilation.py
+++ b/aas_core_codegen/golang/transpilation.py
@@ -1133,6 +1133,56 @@ def transform_function_call(
for arg_node, arg in zip(node.args, args)
]
+ for i, (arg_node, argument) in enumerate(
+ zip(node.args, func_type.func.arguments)
+ ):
+ source_type = intermediate_type_inference.beneath_optional(
+ self.type_map[arg_node]
+ )
+ target_type = intermediate.beneath_optional(argument.type_annotation)
+ if not (
+ isinstance(func_type.func, intermediate.TranspilableVerification)
+ and not argument.mutable
+ and isinstance(
+ source_type, intermediate_type_inference.ListTypeAnnotation
+ )
+ and isinstance(target_type, intermediate.ListTypeAnnotation)
+ and isinstance(
+ source_type.items, intermediate_type_inference.OurTypeAnnotation
+ )
+ and isinstance(target_type.items, intermediate.OurTypeAnnotation)
+ and isinstance(source_type.items.our_type, intermediate.Class)
+ and isinstance(target_type.items.our_type, intermediate.Class)
+ and source_type.items.our_type is not target_type.items.our_type
+ and source_type.items.our_type.is_subclass_of(
+ target_type.items.our_type
+ )
+ ):
+ continue
+
+ # NOTE (mristin):
+ # Go slices are invariant even when their item interfaces inherit.
+ # We convert only mismatching read-only arguments, preserving nil.
+ source_slice = self._our_type_name(
+ golang_naming.interface_name(source_type.items.our_type.name)
+ )
+ target_slice = self._our_type_name(
+ golang_naming.interface_name(target_type.items.our_type.name)
+ )
+ args[i] = Stripped(
+ f"""\
+func(items []{source_slice}) []{target_slice} {{
+{I}if items == nil {{
+{II}return nil
+{I}}}
+{I}result := make([]{target_slice}, len(items))
+{I}for i, item := range items {{
+{II}result[i] = item
+{I}}}
+{I}return result
+}}({args[i]})"""
+ )
+
args_joined = ", ".join(args)
# Apply heuristic for breaking the lines
diff --git a/dev/test_data/common_meta_models/list_of_classes.py b/dev/test_data/common_meta_models/list_of_classes.py
index e43fb6404..a78e6db8c 100644
--- a/dev/test_data/common_meta_models/list_of_classes.py
+++ b/dev/test_data/common_meta_models/list_of_classes.py
@@ -1,5 +1,5 @@
from enum import Enum
-from typing import List
+from typing import List, Optional, Sequence
from icontract import invariant
@@ -17,6 +17,30 @@ def __init__(self, name: str) -> None:
self.name = name
+@verification
+def items_are_some(items: Sequence[Abstract_item]) -> bool:
+ """Check a read-only list of base-class instances."""
+ return all(isinstance(item, Some_item) for item in items)
+
+
+@verification
+def some_items_are_some(items: Sequence[Some_item]) -> bool:
+ """Pass a list of descendants to a base-class list parameter."""
+ return items_are_some(items)
+
+
+@verification
+def optional_some_items_are_some(items: Optional[Sequence[Some_item]]) -> bool:
+ """Preserve an optional descendant list passed to an optional parameter."""
+ return optional_items_are_some(items)
+
+
+@verification
+def optional_items_are_some(items: Optional[Sequence[Abstract_item]]) -> bool:
+ """Check a possibly missing read-only list of base-class instances."""
+ return items is None or items_are_some(items)
+
+
class Another_item(Abstract_item):
serial_number: int
diff --git a/dev/test_data/main/cpp/expected/list_of_classes/expected_output/include/dummy/verification.hpp b/dev/test_data/main/cpp/expected/list_of_classes/expected_output/include/dummy/verification.hpp
index 0dfe6262e..228ced3dc 100644
--- a/dev/test_data/main/cpp/expected/list_of_classes/expected_output/include/dummy/verification.hpp
+++ b/dev/test_data/main/cpp/expected/list_of_classes/expected_output/include/dummy/verification.hpp
@@ -212,6 +212,42 @@ class RecursiveVerification : public IVerification {
const std::shared_ptr& instance_;
}; // class RecursiveVerification
+// region Verification functions
+
+/// \brief Check a read-only list of base-class instances.
+bool ItemsAreSome(
+ const std::vector<
+ std::shared_ptr
+ >& items
+);
+
+/// \brief Pass a list of descendants to a base-class list parameter.
+bool SomeItemsAreSome(
+ const std::vector<
+ std::shared_ptr
+ >& items
+);
+
+/// \brief Preserve an optional descendant list passed to an optional parameter.
+bool OptionalSomeItemsAreSome(
+ const common::optional<
+ std::vector<
+ std::shared_ptr
+ >
+ >& items
+);
+
+/// \brief Check a possibly missing read-only list of base-class instances.
+bool OptionalItemsAreSome(
+ const common::optional<
+ std::vector<
+ std::shared_ptr
+ >
+ >& items
+);
+
+// endregion Verification functions
+
} // namespace verification
/**@}*/
diff --git a/dev/test_data/main/cpp/expected/list_of_classes/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/list_of_classes/expected_output/src/verification.cpp
index 40ac2d582..7a2e0161f 100644
--- a/dev/test_data/main/cpp/expected/list_of_classes/expected_output/src/verification.cpp
+++ b/dev/test_data/main/cpp/expected/list_of_classes/expected_output/src/verification.cpp
@@ -37,6 +37,68 @@ Error::Error(
// endregion struct Error
+// region Verification functions
+
+bool ItemsAreSome(
+ const std::vector<
+ std::shared_ptr
+ >& items
+) {
+ return (
+ common::All(
+ [&](const std::shared_ptr& item) -> bool {
+ return types::IsSomeItem(*item);
+ },
+ items
+ )
+ );
+}
+
+bool SomeItemsAreSome(
+ const std::vector<
+ std::shared_ptr
+ >& items
+) {
+ return (
+ ItemsAreSome(
+ items
+ )
+ );
+}
+
+bool OptionalSomeItemsAreSome(
+ const common::optional<
+ std::vector<
+ std::shared_ptr
+ >
+ >& items
+) {
+ return (
+ OptionalItemsAreSome(
+ items
+ )
+ );
+}
+
+bool OptionalItemsAreSome(
+ const common::optional<
+ std::vector<
+ std::shared_ptr
+ >
+ >& items
+) {
+ return (
+ (
+ (!(items.has_value()))
+ || ItemsAreSome(
+ (*items)
+ )
+ )
+ );
+}
+
+// endregion Verification functions
+
namespace {
/**
diff --git a/dev/test_data/main/csharp/expected/list_of_classes/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/list_of_classes/expected_output/dummy/verification.cs
index 014b3b7be..4b02182fc 100644
--- a/dev/test_data/main/csharp/expected/list_of_classes/expected_output/dummy/verification.cs
+++ b/dev/test_data/main/csharp/expected/list_of_classes/expected_output/dummy/verification.cs
@@ -31,6 +31,48 @@ namespace dummy
///
public static class Verification
{
+ ///
+ /// Check a read-only list of base-class instances.
+ ///
+ public static bool ItemsAreSome(
+ List items
+ )
+ {
+ return items.All(
+ item => item is Our.ISomeItem);
+ } // public static bool ItemsAreSome
+
+ ///
+ /// Pass a list of descendants to a base-class list parameter.
+ ///
+ public static bool SomeItemsAreSome(
+ List items
+ )
+ {
+ return Verification.ItemsAreSome(items);
+ } // public static bool SomeItemsAreSome
+
+ ///
+ /// Preserve an optional descendant list passed to an optional parameter.
+ ///
+ public static bool OptionalSomeItemsAreSome(
+ List? items
+ )
+ {
+ return Verification.OptionalItemsAreSome(items);
+ } // public static bool OptionalSomeItemsAreSome
+
+ ///
+ /// Check a possibly missing read-only list of base-class instances.
+ ///
+ public static bool OptionalItemsAreSome(
+ List? items
+ )
+ {
+ return (items == null)
+ || Verification.ItemsAreSome(items);
+ } // public static bool OptionalItemsAreSome
+
///
/// Hash allowed enum values for efficient validation of enums.
///
diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/internal/xmlcommon/xmlcommon.go
index c0f4987e7..39650fc91 100644
--- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go
index 980c4c87e..32fcc747f 100644
--- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go
+++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go
@@ -584,7 +584,7 @@ func QualifierTypesAreUnique[Q ourtypes.IQualifier](
}
func constructMatchesXMLSerializableString() *regexp.Regexp {
- pattern := "^[\t\n\r -\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]*$"
+ pattern := "^[\\t\\n\\r -\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]*$"
return regexp.MustCompile(
pattern,
@@ -1853,7 +1853,7 @@ func MatchesXsNegativeInteger(text string) bool {
}
func constructMatchesXsString() *regexp.Regexp {
- pattern := "^[\t\n\r -\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]*$"
+ pattern := "^[\\t\\n\\r -\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]*$"
return regexp.MustCompile(
pattern,
diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/common_test.go
index fc537ecd3..aa50c83ee 100644
--- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/aas-core-works/aas-core3.0-golang/ourtesting"
+ ourtypes "github.com/aas-core-works/aas-core3.0-golang/types"
ourxmlization "github.com/aas-core-works/aas-core3.0-golang/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/concrete_classes_test.go
index 1e3b7dea3..8928f110e 100644
--- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestExtensionRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestAdministrativeInformationRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestQualifierRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestAssetAdministrationShellRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestAssetInformationRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -698,7 +698,7 @@ func TestResourceRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -824,7 +824,7 @@ func TestSpecificAssetIDRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -950,7 +950,7 @@ func TestSubmodelRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1076,7 +1076,7 @@ func TestRelationshipElementRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1202,7 +1202,7 @@ func TestSubmodelElementListRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1328,7 +1328,7 @@ func TestSubmodelElementCollectionRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1454,7 +1454,7 @@ func TestPropertyRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1580,7 +1580,7 @@ func TestMultiLanguagePropertyRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1706,7 +1706,7 @@ func TestRangeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1832,7 +1832,7 @@ func TestReferenceElementRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1958,7 +1958,7 @@ func TestBlobRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2084,7 +2084,7 @@ func TestFileRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2210,7 +2210,7 @@ func TestAnnotatedRelationshipElementRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2336,7 +2336,7 @@ func TestEntityRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2462,7 +2462,7 @@ func TestEventPayloadRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2588,7 +2588,7 @@ func TestBasicEventElementRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2714,7 +2714,7 @@ func TestOperationRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2840,7 +2840,7 @@ func TestOperationVariableRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -2966,7 +2966,7 @@ func TestCapabilityRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3092,7 +3092,7 @@ func TestConceptDescriptionRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3218,7 +3218,7 @@ func TestReferenceRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3344,7 +3344,7 @@ func TestKeyRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3470,7 +3470,7 @@ func TestLangStringNameTypeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3596,7 +3596,7 @@ func TestLangStringTextTypeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3722,7 +3722,7 @@ func TestEnvironmentRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3848,7 +3848,7 @@ func TestEmbeddedDataSpecificationRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -3974,7 +3974,7 @@ func TestLevelTypeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4100,7 +4100,7 @@ func TestValueReferencePairRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4226,7 +4226,7 @@ func TestValueListRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4352,7 +4352,7 @@ func TestLangStringPreferredNameTypeIEC61360RoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4478,7 +4478,7 @@ func TestLangStringShortNameTypeIEC61360RoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4604,7 +4604,7 @@ func TestLangStringDefinitionTypeIEC61360RoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -4730,7 +4730,7 @@ func TestDataSpecificationIEC61360RoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/concrete_classes_test.go
index 6e7ebbea9..6d9aa5baa 100644
--- a/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/annotated_assignments/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestChildARoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestChildBRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/arithmetic/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/arithmetic/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/arithmetic/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/arithmetic/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/concrete_classes_test.go
index 101cde10d..97a637b6b 100644
--- a/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/arithmetic/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/assert_statements/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/assert_statements/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/assert_statements/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/assert_statements/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/concrete_classes_test.go
index 71b775558..77f0032d2 100644
--- a/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/assert_statements/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestChildARoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestChildBRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/assignment_targets/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/assignment_targets/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/assignment_targets/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/assignment_targets/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/concrete_classes_test.go
index 18b9efb52..a2bff97f7 100644
--- a/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/assignment_targets/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/concrete_classes_test.go
index d81ac13ee..58d86244f 100644
--- a/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/collections_in_returns/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestGroupedRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
index b3df3ae74..1923ace67 100644
--- a/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/concrete_classes_test.go
index a2e6120cf..2ab1b4cc1 100644
--- a/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/custom_serialization_names/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestQueryConditionRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go
index f884d5a54..840acecd6 100644
--- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestBranchRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestLeafRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestBlossomRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestPlainMarkerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -698,7 +698,7 @@ func TestContainerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/empty_class/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/empty_class/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/empty_class/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/empty_class/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/concrete_classes_test.go
index c24a62964..b3c395902 100644
--- a/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/empty_class/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/enum/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/enum/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/enum/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/enum/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/concrete_classes_test.go
index 34e3ae28e..6c43baaff 100644
--- a/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/enum/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/float_only/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/float_only/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/float_only/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/float_only/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/concrete_classes_test.go
index a8d8d7892..92cefa00a 100644
--- a/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/float_only/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/for_statements/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/for_statements/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/for_statements/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/for_statements/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/concrete_classes_test.go
index 4dc2b178a..911e758f2 100644
--- a/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/for_statements/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/if_statements/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/if_statements/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/if_statements/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/if_statements/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/concrete_classes_test.go
index ba618a7d6..1d04f08ca 100644
--- a/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/if_statements/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestChildARoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestChildBRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestContainerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/implementation_specific/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/implementation_specific/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/implementation_specific/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/implementation_specific/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/concrete_classes_test.go
index c51c6cee9..a985e7ac8 100644
--- a/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/implementation_specific/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestBoxRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestBagRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestContainerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/concrete_classes_test.go
index 342ae9cf5..c0f34ee3b 100644
--- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/concrete_classes_test.go
index 37679af6c..95e2192ca 100644
--- a/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/int_parsing_and_lstrip/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/is_instance/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/is_instance/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/is_instance/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/is_instance/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/concrete_classes_test.go
index 2b03b8be8..ea7dc48d6 100644
--- a/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/is_instance/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestLeafRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestOrderedContainerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestUnorderedContainerRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestGlobalAttributeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestLocalAttributeRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -698,7 +698,7 @@ func TestAttributeOperandRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -824,7 +824,7 @@ func TestStringLiteralRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -950,7 +950,7 @@ func TestNumberLiteralRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1076,7 +1076,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/json_types/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/json_types/expected_output/internal/xmlcommon/xmlcommon.go
index af2fbf335..bd2aad58d 100644
--- a/dev/test_data/main/golang/expected/json_types/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/json_types/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/concrete_classes_test.go
index 2114c06bd..224816e07 100644
--- a/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/json_types/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/internal/xmlcommon/xmlcommon.go
index af2fbf335..bd2aad58d 100644
--- a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/concrete_classes_test.go
index a2f2029b3..70d88f768 100644
--- a/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/json_types_in_collections/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/internal/xmlcommon/xmlcommon.go
index af2fbf335..bd2aad58d 100644
--- a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/concrete_classes_test.go
index a2571f811..97badc577 100644
--- a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/list_of_classes/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/list_of_classes/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/list_of_classes/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/list_of_classes/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/list_of_classes/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/list_of_classes/expected_output/verification/verification.go
index 26a54b221..90f60efc8 100644
--- a/dev/test_data/main/golang/expected/list_of_classes/expected_output/verification/verification.go
+++ b/dev/test_data/main/golang/expected/list_of_classes/expected_output/verification/verification.go
@@ -12,6 +12,7 @@ package verification
import (
"fmt"
+ ourcommon "github.com/dummy-works/dummy/common"
ourreporting "github.com/dummy-works/dummy/reporting"
ourtypes "github.com/dummy-works/dummy/types"
)
@@ -44,6 +45,62 @@ func (ve *VerificationError) PathString() string {
return ourreporting.ToGolangPath(ve.Path)
}
+// Check a read-only list of base-class instances.
+func ItemsAreSome(
+ items []ourtypes.IAbstractItem,
+) bool {
+ return ourcommon.All(
+ func(item ourtypes.IAbstractItem) bool {
+ return ourtypes.IsSomeItem(item)
+ },
+ items,
+ )
+}
+
+// Pass a list of descendants to a base-class list parameter.
+func SomeItemsAreSome(
+ items []ourtypes.ISomeItem,
+) bool {
+ return ItemsAreSome(
+ func(items []ourtypes.ISomeItem) []ourtypes.IAbstractItem {
+ if items == nil {
+ return nil
+ }
+ result := make([]ourtypes.IAbstractItem, len(items))
+ for i, item := range items {
+ result[i] = item
+ }
+ return result
+ }(items),
+ )
+}
+
+// Preserve an optional descendant list passed to an optional parameter.
+func OptionalSomeItemsAreSome(
+ items []ourtypes.ISomeItem,
+) bool {
+ return OptionalItemsAreSome(
+ func(items []ourtypes.ISomeItem) []ourtypes.IAbstractItem {
+ if items == nil {
+ return nil
+ }
+ result := make([]ourtypes.IAbstractItem, len(items))
+ for i, item := range items {
+ result[i] = item
+ }
+ return result
+ }(items),
+ )
+}
+
+// Check a possibly missing read-only list of base-class instances.
+func OptionalItemsAreSome(
+ items []ourtypes.IAbstractItem,
+) bool {
+ return (items == nil) ||
+ ItemsAreSome(items)
+}
+
// Verify `that` instance of [ourtypes.ISomeItem].
//
// You have to supply the callback `onError` to iterate over the errors.
diff --git a/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/concrete_classes_test.go
index 797e4f988..dbd32a783 100644
--- a/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/list_of_classes/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomeItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestAnotherItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestSimpleRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
index aa958a005..e0287ac19 100644
--- a/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/list_of_constrained_primitives/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/list_of_enums/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/list_of_enums/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/list_of_enums/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/list_of_enums/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/concrete_classes_test.go
index 528ecc4de..6fbd8bb00 100644
--- a/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/list_of_enums/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/concrete_classes_test.go
index 186fd0ba2..9685bdf99 100644
--- a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/method_references/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/method_references/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/method_references/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/method_references/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/concrete_classes_test.go
index acb50f616..ef290fd61 100644
--- a/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/method_references/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestBoxRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/concrete_classes_test.go
index bb4b90cdd..4b3e82bfb 100644
--- a/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/nested_any_in_all/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestLangStringRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestLangStringSetRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestIecContentRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestOtherContentRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestSpecificationRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -698,7 +698,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/nested_collections/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/nested_collections/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/nested_collections/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/nested_collections/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/concrete_classes_test.go
index 203dd7301..b06c770d2 100644
--- a/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/nested_collections/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomeItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestAnotherItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/optional_arguments/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/optional_arguments/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/optional_arguments/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/optional_arguments/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/concrete_classes_test.go
index b5d7bc5e9..c87eabe5d 100644
--- a/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/optional_arguments/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/primitive_types/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/primitive_types/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/primitive_types/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/primitive_types/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/concrete_classes_test.go
index e40e4c204..7278cb63d 100644
--- a/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/primitive_types/expected_output/xmlization/test/concrete_classes_test.go
@@ -69,7 +69,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/concrete_classes_test.go
index 05552338d..599c7624a 100644
--- a/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/problematic_keywords/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/sets/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/sets/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/sets/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/sets/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/concrete_classes_test.go
index c78bd3bb7..3cb41d7f7 100644
--- a/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/sets/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestCollectionRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/concrete_classes_test.go
index 66b7f602d..ab7e0adf9 100644
--- a/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/string_slicing_and_find/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/switch_statements/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/switch_statements/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/switch_statements/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/switch_statements/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/concrete_classes_test.go
index 5fc54fc5a..d37f9adfb 100644
--- a/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/switch_statements/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/concrete_classes_test.go
index 6250f002d..0d4f0603f 100644
--- a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestFirstRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestSecondRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/tuples/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/tuples/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/tuples/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/tuples/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/concrete_classes_test.go
index 203dd7301..b06c770d2 100644
--- a/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/tuples/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestSomeItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestAnotherItemRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/unions/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/unions/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/unions/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/unions/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/concrete_classes_test.go
index ffaf16fea..ea3997045 100644
--- a/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/unions/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestStructuralFirstRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -194,7 +194,7 @@ func TestStructuralSecondRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -320,7 +320,7 @@ func TestMixedAbstractDescendantOneRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -446,7 +446,7 @@ func TestMixedAbstractDescendantTwoRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -572,7 +572,7 @@ func TestMixedConcreteWithDescendantsRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -698,7 +698,7 @@ func TestMixedConcreteWithDescendantsChildRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -824,7 +824,7 @@ func TestMixedConcreteLeafRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -950,7 +950,7 @@ func TestModelTypedFirstRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1076,7 +1076,7 @@ func TestModelTypedSecondRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
@@ -1202,7 +1202,7 @@ func TestSomethingRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/golang/expected/visibility/expected_output/internal/xmlcommon/xmlcommon.go b/dev/test_data/main/golang/expected/visibility/expected_output/internal/xmlcommon/xmlcommon.go
index 436dbd116..e7bcae2b2 100644
--- a/dev/test_data/main/golang/expected/visibility/expected_output/internal/xmlcommon/xmlcommon.go
+++ b/dev/test_data/main/golang/expected/visibility/expected_output/internal/xmlcommon/xmlcommon.go
@@ -837,6 +837,11 @@ func readElementDispatchedInNamespace[T any](
return
}
+ current, err = SkipEmptyTextWhitespaceAndComments(decoder, current)
+ if err != nil {
+ return
+ }
+
err = checkEndElementInNamespace(current, local, ns)
if err != nil {
return
diff --git a/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/common_test.go b/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/common_test.go
index f092c4c7d..edefb227a 100644
--- a/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/common_test.go
+++ b/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/common_test.go
@@ -4,13 +4,14 @@ package xmlization_test
// Do NOT edit or append.
import (
+ "encoding/xml"
"fmt"
"os"
"path/filepath"
- "regexp"
"strings"
"testing"
ourtesting "github.com/dummy-works/dummy/ourtesting"
+ ourtypes "github.com/dummy-works/dummy/types"
ourxmlization "github.com/dummy-works/dummy/xmlization"
)
@@ -53,148 +54,25 @@ func assertNoSerializationError(
return
}
-// NOTE (mristin):
-// Currently, Go does not support self-closing tags,
-// see: https://github.com/golang/go/issues/21399.
-// We apply the following hack to make the tags self-closing even if they are not.
-// This is unsafe in general, but works OK for the limited set of test data that we are
-// here dealing with.
-//
-// The code has been taken from: https://github.com/golang/go/issues/21399#issuecomment-1342730174
-
-// NOTE (mristin):
-// The opening tag, tag name plus an optional attribute list (*e.g.*,
-// ``xmlns="..."`` on a root element), is captured in the first (and only)
-// submatch group so that we do not lose any attributes when we force
-// the tag to be self-closing below. The attribute list must not end with
-// a slash, as we would otherwise match an already self-closing tag followed
-// by the closing tag of its parent, *e.g.*, ````.
-var emptyTagRe = regexp.MustCompile(`<(\w+(?:\s[^>]*[^/>])?)>\w+>`)
-
-func forceSelfClosingTags(text string) string {
- b := []byte(text)
- emptyTagIdxs := emptyTagRe.FindAllSubmatchIndex(b, -1)
-
- if len(emptyTagIdxs) == 0 {
- return text
- }
-
- // NOTE (mristin):
- // The matches are walked in order, and the pieces of `b` between them are
- // copied over as they are. Mind that `nb` starts out empty and that `b` is
- // only ever *read*: an earlier version appended into `b[0:idx[0]]`, which
- // writes into `b`'s own backing array, so a document with more than one
- // empty tag came out garbled -- every turn of the loop then indexed into
- // a `b` which the previous turn had already overwritten.
- var nb []byte
- previous := 0
-
- for _, idx := range emptyTagIdxs {
- // Copy everything since the previous match, then the tag itself --
- // its name and its attributes -- as a self-closing one.
- nb = append(nb, b[previous:idx[0]]...)
- nb = append(nb, fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...)
-
- previous = idx[1]
- }
-
- // Finally, copy everything after the last match.
- nb = append(nb, b[previous:]...)
-
- return string(nb)
-}
-
-var whitespaceBetweenTagsRe = regexp.MustCompile(`>\s+<`)
-
-// Remove all whitespace (including newlines, tabs, and spaces) between XML tags
-// without parsing the XML.
-func removeWhitespaceBetweenTags(text string) string {
- return whitespaceBetweenTagsRe.ReplaceAllString(text, "><")
-}
-
-var whitespaceBeforeAngleBracketsRe = regexp.MustCompile(`\s+>`)
-var whitespaceBeforeSelfClosingRe = regexp.MustCompile(`\s+/>`)
-var whitespaceAfterAngleBracketsRe = regexp.MustCompile(`<\s+`)
-
-// Remove any whitespace before `>` and `/>` or after `<`
-func removeWhitespaceWithinTags(text string) string {
- result := text
- result = whitespaceBeforeAngleBracketsRe.ReplaceAllString(result, ">")
- result = whitespaceBeforeSelfClosingRe.ReplaceAllString(result, "/>")
- result = whitespaceAfterAngleBracketsRe.ReplaceAllString(result, "<")
- return result
-}
-
-// Assert that the serialization `other`, as XML document, equals the original
-//
-// XML document `that` read from the `source`.
+// Assert that serializing and de-serializing preserves the original instance.
func assertSerializationEqualsDeserialization(
t *testing.T,
- that string,
+ that ourtypes.IClass,
other string,
source string,
) (ok bool) {
- ok = true
-
- // Remove carriers to avoid problems between Windows, Posix and MacOS
- canonicalThat := strings.ReplaceAll(that, "\r", "")
- canonicalOther := strings.ReplaceAll(other, "\r", "")
-
- canonicalThat = strings.TrimSpace(canonicalThat)
- canonicalOther = strings.TrimSpace(canonicalOther)
-
- canonicalThat = forceSelfClosingTags(canonicalThat)
- canonicalOther = forceSelfClosingTags(canonicalOther)
-
- // NOTE (mristin):
- // The following hack is SUPER ugly and unsafe! However, it works. Given Go's
- // limited support for XML, we gave up on a safer approach :(. We tested
- // the following approaches before applying this hack:
- // * A round-trip over `encoding/xml`. Failed due to
- // https://github.com/golang/go/issues/13400.
- // * Using `aqwari.net/xml/xmltree`. Failed as the special characters in the
- // element content still has not been de-escaped or consistently escaped in
- // a round trip.
-
- canonicalThat = strings.ReplaceAll(canonicalThat, "'", "'")
- canonicalOther = strings.ReplaceAll(canonicalOther, "'", "'")
-
- canonicalThat = removeWhitespaceBetweenTags(canonicalThat)
- canonicalOther = removeWhitespaceBetweenTags(canonicalOther)
-
- canonicalThat = removeWhitespaceWithinTags(canonicalThat)
- canonicalOther = removeWhitespaceWithinTags(canonicalOther)
-
- thatLines := strings.Split(canonicalThat, "\n")
- otherLines := strings.Split(canonicalOther, "\n")
-
- if canonicalThat != canonicalOther {
- b := new(strings.Builder)
- minLines := len(thatLines)
- if minLines > len(otherLines) {
- minLines = len(otherLines)
- }
- for i := 0; i < minLines; i++ {
- if thatLines[i] == otherLines[i] {
- b.WriteString(fmt.Sprintf(" %s\n", thatLines[i]))
- } else {
- b.WriteString(fmt.Sprintf("ORIGINAL %s\n", thatLines[i]))
- b.WriteString(fmt.Sprintf("SERIALIZED %s\n", otherLines[i]))
- break
- }
- }
+ decoder := xml.NewDecoder(strings.NewReader(other))
+ roundTripped, err := ourxmlization.Unmarshal(decoder)
+ if !assertNoDeserializationError(t, err, source) {
+ return false
+ }
- ok = false
- t.Fatalf(
- "The canonicalized XML serialization of the de-serialized instance "+
- "from %s does not equal the canonicalized original XML document:\n"+
- "%s",
- source, b.String(),
- )
- return
+ if !ourtesting.DeepEqual(that, roundTripped) {
+ t.Fatalf("XML round-trip changed the instance from %s", source)
+ return false
}
- return
+ return true
}
// Assert that there is a de-serialization error.
diff --git a/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/concrete_classes_test.go
index c300e0870..2a7ef9e3c 100644
--- a/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/concrete_classes_test.go
+++ b/dev/test_data/main/golang/expected/visibility/expected_output/xmlization/test/concrete_classes_test.go
@@ -68,7 +68,7 @@ func TestBoxRoundTripOK(t *testing.T) {
ok = assertSerializationEqualsDeserialization(
t,
- text,
+ deserialized,
roundTrip,
pth,
)
diff --git a/dev/test_data/main/java/expected/list_of_classes/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/list_of_classes/expected_output/src/main/java/dummy/verification/Verification.java
index 7beb43f66..5e27769f0 100644
--- a/dev/test_data/main/java/expected/list_of_classes/expected_output/src/main/java/dummy/verification/Verification.java
+++ b/dev/test_data/main/java/expected/list_of_classes/expected_output/src/main/java/dummy/verification/Verification.java
@@ -28,8 +28,43 @@
import dummy.types.model.*;
import dummy.visitation.AbstractTransformer;
import java.util.List;
+import java.util.Optional;
public class Verification {
+ /**
+ * Check a read-only list of base-class instances.
+ */
+ public static Boolean itemsAreSome(
+ List items) {
+ return items.stream().allMatch(
+ item -> item instanceof ISomeItem);
+ }
+
+ /**
+ * Pass a list of descendants to a base-class list parameter.
+ */
+ public static Boolean someItemsAreSome(
+ List items) {
+ return itemsAreSome(items);
+ }
+
+ /**
+ * Preserve an optional descendant list passed to an optional parameter.
+ */
+ public static Boolean optionalSomeItemsAreSome(
+ Optional> items) {
+ return optionalItemsAreSome(items);
+ }
+
+ /**
+ * Check a possibly missing read-only list of base-class instances.
+ */
+ public static Boolean optionalItemsAreSome(
+ Optional> items) {
+ return (!items.isPresent())
+ || itemsAreSome(items.orElse(null));
+ }
+
/**
* Hash allowed enum values for efficient validation of enums.
*/
diff --git a/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
index a68d63f87..5b328630e 100644
--- a/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
+++ b/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
@@ -62,6 +62,50 @@
Error = our_reporting.Error
+def items_are_some(
+ items: Sequence[our_types.AbstractItem]
+) -> bool:
+ """Check a read-only list of base-class instances."""
+ # pylint: disable=all
+ return (
+ all(
+ isinstance(item, our_types.SomeItem)
+ for item in items
+ ))
+
+
+def some_items_are_some(
+ items: Sequence[our_types.SomeItem]
+) -> bool:
+ """Pass a list of descendants to a base-class list parameter."""
+ # pylint: disable=all
+ return items_are_some(items)
+
+
+def optional_some_items_are_some(
+ items: Optional[Sequence[our_types.SomeItem]]
+) -> bool:
+ """
+ Preserve an optional descendant list passed to an optional parameter.
+ """
+ # pylint: disable=all
+ return optional_items_are_some(items)
+
+
+def optional_items_are_some(
+ items: Optional[Sequence[our_types.AbstractItem]]
+) -> bool:
+ """
+ Check a possibly missing read-only list of base-class instances.
+ """
+ # pylint: disable=all
+ return (
+ (
+ (items is None)
+ or items_are_some(items)
+ ))
+
+
class _Transformer(
our_types.AbstractTransformer[
Iterator[Error]
diff --git a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
index 2538f6bcf..20a87e801 100644
--- a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
+++ b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
@@ -189,6 +189,51 @@ export class VerificationError {
}
}
+/**
+ * Check a read-only list of base-class instances.
+ */
+export function itemsAreSome(
+ items: Array
+): boolean {
+ return OurCommon.every(
+ OurCommon.map(
+ items,
+ item =>
+ OurTypes.isSomeItem(item)
+ )
+ );
+}
+
+/**
+ * Pass a list of descendants to a base-class list parameter.
+ */
+export function someItemsAreSome(
+ items: Array
+): boolean {
+ return itemsAreSome(items);
+}
+
+/**
+ * Preserve an optional descendant list passed to an optional parameter.
+ */
+export function optionalSomeItemsAreSome(
+ items: Array | null
+): boolean {
+ return optionalItemsAreSome(items);
+}
+
+/**
+ * Check a possibly missing read-only list of base-class instances.
+ */
+export function optionalItemsAreSome(
+ items: Array | null
+): boolean {
+ return (
+ (items === null)
+ || itemsAreSome(items)
+ );
+}
+
/**
* Verify the items of `that` recursively.
*/
diff --git a/dev/tests/golang/test_verification.py b/dev/tests/golang/test_verification.py
new file mode 100644
index 000000000..4091e4c5b
--- /dev/null
+++ b/dev/tests/golang/test_verification.py
@@ -0,0 +1,35 @@
+# pylint: disable=missing-docstring
+
+import re
+import unittest
+
+from aas_core_codegen.golang.lib import _generate_verification as golang_verification
+from aas_core_codegen.parse import retree as parse_retree
+
+
+class TestRegexRenderer(unittest.TestCase):
+ def test_encoded_metacharacters(self) -> None:
+ for pattern in [
+ r"^\x5Btext\x5D$",
+ r"^[\x5B\x5D\x5E\x2D\x5C]+$",
+ r"^[\x20-\x5B\x5D-\uD7FF]+$",
+ ]:
+ with self.subTest(pattern=pattern):
+ parsed, error = parse_retree.parse([pattern])
+ self.assertIsNone(error)
+ assert parsed is not None
+ rendered = parse_retree.render(
+ parsed, golang_verification.RegexRenderer()
+ )
+ self.assertEqual(1, len(rendered))
+ assert isinstance(rendered[0], str)
+ for text in ["[text]", "[", "]", "^", "-", "\\", "text", "\uD7FF"]:
+ with self.subTest(text=text):
+ self.assertEqual(
+ re.fullmatch(pattern, text) is not None,
+ re.fullmatch(rendered[0], text) is not None,
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()