diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml
index c180fa95..e81ddc21 100644
--- a/ballerina/Ballerina.toml
+++ b/ballerina/Ballerina.toml
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "xmldata"
-version = "2.6.0"
+version = "2.6.1"
authors = ["Ballerina"]
keywords = ["xml", "json"]
repository = "https://github.com/ballerina-platform/module-ballerina-xmldata"
@@ -15,5 +15,5 @@ graalvmCompatible = true
[[platform.java11.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "xmldata-native"
-version = "2.6.0"
-path = "../native/build/libs/xmldata-native-2.6.0.jar"
+version = "2.6.1"
+path = "../native/build/libs/xmldata-native-2.6.1-SNAPSHOT.jar"
diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml
index 0733a067..13d37ede 100644
--- a/ballerina/CompilerPlugin.toml
+++ b/ballerina/CompilerPlugin.toml
@@ -3,4 +3,4 @@ id = "xmldata-compiler-plugin"
class = "io.ballerina.stdlib.xmldata.compiler.XmldataCompilerPlugin"
[[dependency]]
-path = "../compiler-plugin/build/libs/xmldata-compiler-plugin-2.6.0.jar"
+path = "../compiler-plugin/build/libs/xmldata-compiler-plugin-2.6.1-SNAPSHOT.jar"
diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml
index bd6e0abe..d0f6684d 100644
--- a/ballerina/Dependencies.toml
+++ b/ballerina/Dependencies.toml
@@ -40,7 +40,7 @@ modules = [
[[package]]
org = "ballerina"
name = "xmldata"
-version = "2.6.0"
+version = "2.6.1"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "test"}
diff --git a/ballerina/tests/from_xml_test.bal b/ballerina/tests/from_xml_test.bal
index a441ce8b..750e738e 100644
--- a/ballerina/tests/from_xml_test.bal
+++ b/ballerina/tests/from_xml_test.bal
@@ -1109,3 +1109,192 @@ isolated function testFromXmlWithBackSlash1() returns error? {
Book_Store actual = check fromXml(payload);
test:assertEquals(actual, expected, msg = "testToRecordWithNamespaces result incorrect");
}
+
+
+type BookStore7 record {
+ string storeName;
+ int postalCode;
+ @Name {
+ value: "isOpen"
+ }
+ boolean open;
+ Address7 address;
+ Codes7 codes;
+ @Attribute
+ string status;
+ @Attribute
+ string 'xmlns\:ns0;
+};
+
+type Address7 record {
+ string street;
+ string city;
+ string country;
+};
+
+type Codes7 record {
+ int[] item;
+};
+
+@test:Config {
+ groups: ["fromXml"]
+}
+isolated function testRecordToXml7() returns error? {
+ xml payload = xml `
+ foo
+ 94
+ true
+
+ Galle Road
+ Colombo
+ Sri Lanka
+
+
+ - 4
+ - 8
+ - 9
+
+
+
+ `;
+ BookStore7 expected = {
+ storeName: "foo",
+ postalCode: 94,
+ open: true,
+ address: {
+ street: "Galle Road",
+ city: "Colombo",
+ country: "Sri Lanka"
+ },
+ codes: {
+ item: [4, 8, 9]
+ },
+ 'xmlns\:ns0: "http://sample.com/test",
+ status: "online"
+ };
+ BookStore7 actual = check fromXml(payload);
+ test:assertEquals(actual, expected, msg = "testToRecordWithNamespaces result incorrect");
+}
+
+@Name {
+ value: "BookStore8"
+}
+type BookStore9 record {
+ string storeName;
+ int postalCode;
+ boolean isOpen;
+ @Name {
+ value: "address"
+ }
+ Address8 addr;
+ @Name {
+ value: "codes"
+ }
+ Codes8 code;
+ @Attribute
+ string status;
+ @Attribute
+ string 'xmlns\:ns0;
+};
+
+type Address8 record {
+ string street;
+ string city;
+ string country;
+};
+
+type Codes8 record {
+ int[] item;
+};
+
+@test:Config {
+ groups: ["fromXml"]
+}
+isolated function testRecordToXml8() returns error? {
+ xml payload = xml `
+ foo
+ 94
+ true
+
+ Galle Road
+ Colombo
+ Sri Lanka
+
+
+ - 4
+ - 8
+ - 9
+
+
+
+ `;
+ BookStore9 expected = {
+ storeName: "foo",
+ postalCode: 94,
+ isOpen: true,
+ addr: {
+ street: "Galle Road",
+ city: "Colombo",
+ country: "Sri Lanka"
+ },
+ code: {
+ item: [4, 8, 9]
+ },
+ 'xmlns\:ns0: "http://sample.com/test",
+ status: "online"
+ };
+ BookStore9 actual = check fromXml(payload);
+ test:assertEquals(actual, expected, msg = "testToRecordWithNamespaces result incorrect");
+}
+
+type Appointment record {
+ string firstName;
+ string lastName;
+ string email;
+ string age;
+};
+
+@Name {
+ value: "appointments"
+}
+type Appointments record {
+ Appointment[] appointment;
+};
+
+@test:Config {
+ groups: ["fromXml"]
+}
+isolated function testFromXmlWithNameAnnotation() returns error? {
+ Appointments expected = {
+ "appointment": [
+ {
+ "firstName":"John",
+ "lastName":"Doe",
+ "email":"john.doe@gmail.com",
+ "age":"28"
+ },
+ {
+ "firstName":"John",
+ "lastName":"Doe",
+ "email":"john.doe@gmail.com",
+ "age":"28"
+ }
+ ]
+ };
+ xml xmlPayload = xml `
+
+ John
+ Doe
+ john.doe@gmail.com
+ 28
+
+
+ John
+ Doe
+ john.doe@gmail.com
+ 28
+
+ `;
+ Appointments result = check fromXml(xmlPayload);
+ test:assertEquals(result, expected, msg = "testFromXmlWithNameAnnotation result incorrect");
+}
diff --git a/changelog.md b/changelog.md
index 55696c02..fc67aaf0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Fixed
+[Fix the mismatch error by `fromXml` API while the field has the name annotation](https://github.com/ballerina-platform/ballerina-standard-library/issues/3802)
+
+## [2.4.3] - 2023-05-12
+
### Fixed
- [Fix the bug from fromXml API when xml elements have different namespaces](https://github.com/ballerina-platform/ballerina-standard-library/issues/4434)
diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/xmldata/compiler/CompilerPluginTest.java
index 05152937..2c8d36fa 100644
--- a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/xmldata/compiler/CompilerPluginTest.java
+++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/xmldata/compiler/CompilerPluginTest.java
@@ -107,7 +107,7 @@ public void testInvalidType5() {
List errorDiagnosticsList = diagnosticResult.diagnostics().stream()
.filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
.collect(Collectors.toList());
- Assert.assertEquals(errorDiagnosticsList.size(), 5);
+ Assert.assertEquals(errorDiagnosticsList.size(), 6);
}
@Test
@@ -116,7 +116,7 @@ public void testInvalidType6() {
List errorDiagnosticsList = diagnosticResult.diagnostics().stream()
.filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
.collect(Collectors.toList());
- Assert.assertEquals(errorDiagnosticsList.size(), 3);
+ Assert.assertEquals(errorDiagnosticsList.size(), 4);
}
@Test
@@ -125,7 +125,7 @@ public void testInvalidType7() {
List errorDiagnosticsList = diagnosticResult.diagnostics().stream()
.filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
.collect(Collectors.toList());
- Assert.assertEquals(errorDiagnosticsList.size(), 2);
+ Assert.assertEquals(errorDiagnosticsList.size(), 3);
}
@Test
@@ -134,7 +134,7 @@ public void testInvalidType8() {
List errorDiagnosticsList = diagnosticResult.diagnostics().stream()
.filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
.collect(Collectors.toList());
- Assert.assertEquals(errorDiagnosticsList.size(), 4);
+ Assert.assertEquals(errorDiagnosticsList.size(), 5);
}
@Test
@@ -153,9 +153,20 @@ public void testInvalidUnionType1() {
.filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
.collect(Collectors.toList());
Assert.assertEquals(errorDiagnosticsList.size(), 2);
- Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(),
- "invalid field type: the record field does not support the optional value type");
Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(),
+ "invalid field type: the record field does not support the optional value type");
+ Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(),
"invalid union type: union type does not support multiple non-primitive record types");
}
+
+ @Test
+ public void testInvalidChildAnnotation() {
+ DiagnosticResult diagnosticResult = loadPackage("sample11").getCompilation().diagnosticResult();
+ List errorDiagnosticsList = diagnosticResult.diagnostics().stream()
+ .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR))
+ .collect(Collectors.toList());
+ Assert.assertEquals(errorDiagnosticsList.size(), 1);
+ Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(),
+ "invalid annotation attachment: child record does not allow name annotation");
+ }
}
diff --git a/compiler-plugin-tests/src/test/resources/diagnostics/sample1/main.bal b/compiler-plugin-tests/src/test/resources/diagnostics/sample1/main.bal
index c94287ac..6967c59f 100644
--- a/compiler-plugin-tests/src/test/resources/diagnostics/sample1/main.bal
+++ b/compiler-plugin-tests/src/test/resources/diagnostics/sample1/main.bal
@@ -21,7 +21,7 @@ type Foo record {
};
type Bar record {
- int? bar;
+ int? bar = 2;
string car;
};
diff --git a/compiler-plugin-tests/src/test/resources/diagnostics/sample10/Ballerina.toml b/compiler-plugin-tests/src/test/resources/diagnostics/sample10/Ballerina.toml
index 1d3e071a..d945d518 100644
--- a/compiler-plugin-tests/src/test/resources/diagnostics/sample10/Ballerina.toml
+++ b/compiler-plugin-tests/src/test/resources/diagnostics/sample10/Ballerina.toml
@@ -1,4 +1,4 @@
[package]
org = "xmldata_test"
-name = "sample9"
+name = "sample10"
version = "0.1.0"
diff --git a/compiler-plugin-tests/src/test/resources/diagnostics/sample11/Ballerina.toml b/compiler-plugin-tests/src/test/resources/diagnostics/sample11/Ballerina.toml
new file mode 100644
index 00000000..bec3efd9
--- /dev/null
+++ b/compiler-plugin-tests/src/test/resources/diagnostics/sample11/Ballerina.toml
@@ -0,0 +1,4 @@
+[package]
+org = "xmldata_test"
+name = "sample11"
+version = "0.1.0"
diff --git a/compiler-plugin-tests/src/test/resources/diagnostics/sample11/main.bal b/compiler-plugin-tests/src/test/resources/diagnostics/sample11/main.bal
new file mode 100644
index 00000000..7232ae1d
--- /dev/null
+++ b/compiler-plugin-tests/src/test/resources/diagnostics/sample11/main.bal
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+//
+// WSO2 LLC. licenses this file to you under the Apache License,
+// Version 2.0 (the "License"); you may not use this file except
+// in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import ballerina/xmldata;
+
+@xmldata:Name {
+ value: "Foo1"
+}
+type Foo record {
+ Bar foo;
+};
+
+@xmldata:Name {
+ value: "Bar1"
+}
+type Bar record {
+ int bar;
+ Bar2 bar2;
+};
+
+@xmldata:Name {
+ value: "Bar4"
+}
+@xmldata:Namespace {
+ prefix: "ns",
+ uri: "http://sdf.com"
+}
+type Bar2 record {
+ int bar;
+ string car;
+};
+
+public function main() returns error? {
+ xml x1 = xml `2`;
+ Foo actual = check xmldata:fromXml(x1);
+ Bar result = check xmldata:fromXml(x1);
+}
diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/DiagnosticsCodes.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/DiagnosticsCodes.java
index fa376bf1..0c22c2fa 100644
--- a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/DiagnosticsCodes.java
+++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/DiagnosticsCodes.java
@@ -29,8 +29,9 @@ public enum DiagnosticsCodes {
XMLDATA_101("XMLDATA_101",
"invalid field type: the record field does not support the optional value type", ERROR),
XMLDATA_102("XMLDATA_102",
- "invalid union type: union type does not support multiple non-primitive record types", ERROR);
-
+ "invalid union type: union type does not support multiple non-primitive record types", ERROR),
+ XMLDATA_103("XMLDATA_103",
+ "invalid annotation attachment: child record does not allow name annotation", ERROR);
private final String code;
private final String message;
private final DiagnosticSeverity severity;
diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataCodeAnalyzer.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataCodeAnalyzer.java
index 44bba545..441cadcd 100644
--- a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataCodeAnalyzer.java
+++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataCodeAnalyzer.java
@@ -32,6 +32,6 @@ public class XmldataCodeAnalyzer extends CodeAnalyzer {
@Override
public void init(CodeAnalysisContext codeAnalysisContext) {
codeAnalysisContext.addSyntaxNodeAnalysisTask(new XmldataRecordFieldValidator(),
- List.of(SyntaxKind.LOCAL_VAR_DECL, SyntaxKind.MODULE_VAR_DECL, SyntaxKind.RECORD_FIELD));
+ List.of(SyntaxKind.MODULE_PART));
}
}
diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataRecordFieldValidator.java
index 21a3d800..8e761d2b 100644
--- a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataRecordFieldValidator.java
+++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/XmldataRecordFieldValidator.java
@@ -17,39 +17,54 @@
*/
package io.ballerina.stdlib.xmldata.compiler;
-import io.ballerina.compiler.api.symbols.ArrayTypeSymbol;
-import io.ballerina.compiler.api.symbols.NilTypeSymbol;
-import io.ballerina.compiler.api.symbols.RecordFieldSymbol;
-import io.ballerina.compiler.api.symbols.Symbol;
-import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol;
-import io.ballerina.compiler.api.symbols.TypeSymbol;
-import io.ballerina.compiler.api.symbols.UnionTypeSymbol;
+import io.ballerina.compiler.syntax.tree.AnnotationNode;
+import io.ballerina.compiler.syntax.tree.ArrayTypeDescriptorNode;
+import io.ballerina.compiler.syntax.tree.CheckExpressionNode;
+import io.ballerina.compiler.syntax.tree.ChildNodeList;
import io.ballerina.compiler.syntax.tree.ExpressionNode;
+import io.ballerina.compiler.syntax.tree.FunctionCallExpressionNode;
+import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
+import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode;
+import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode;
+import io.ballerina.compiler.syntax.tree.NilTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.Node;
+import io.ballerina.compiler.syntax.tree.NodeList;
+import io.ballerina.compiler.syntax.tree.OptionalTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.RecordFieldNode;
-import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode;
+import io.ballerina.compiler.syntax.tree.RecordFieldWithDefaultValueNode;
+import io.ballerina.compiler.syntax.tree.RecordTypeDescriptorNode;
+import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode;
+import io.ballerina.compiler.syntax.tree.SyntaxKind;
+import io.ballerina.compiler.syntax.tree.TypeDefinitionNode;
+import io.ballerina.compiler.syntax.tree.TypeDescriptorNode;
+import io.ballerina.compiler.syntax.tree.UnionTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.VariableDeclarationNode;
import io.ballerina.projects.plugins.AnalysisTask;
import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext;
+import io.ballerina.stdlib.xmldata.compiler.object.Record;
import io.ballerina.tools.diagnostics.Diagnostic;
import io.ballerina.tools.diagnostics.DiagnosticFactory;
import io.ballerina.tools.diagnostics.DiagnosticInfo;
import io.ballerina.tools.diagnostics.DiagnosticSeverity;
+import io.ballerina.tools.diagnostics.Location;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
/**
* Xmldata record field analyzer.
*/
public class XmldataRecordFieldValidator implements AnalysisTask {
-
- private final List nodes = new ArrayList<>();
- private final List validatedNodes = new ArrayList<>();
+ private final Map records = new HashMap<>();
+ private final List recordNamesUsedInFunction = new ArrayList<>();
+ private final List validatedRecords = new ArrayList<>();
private static final String TO_RECORD = "xmldata:toRecord";
private static final String FROM_XML = "xmldata:fromXml";
+ private static final String NAME_ANNOTATION = "xmldata:Name";
@Override
public void perform(SyntaxNodeAnalysisContext ctx) {
@@ -60,107 +75,165 @@ public void perform(SyntaxNodeAnalysisContext ctx) {
}
}
- Node node = ctx.node();
- if (node instanceof RecordFieldNode) {
- this.nodes.add(ctx);
+ ModulePartNode rootNode = (ModulePartNode) ctx.node();
+ for (ModuleMemberDeclarationNode member : rootNode.members()) {
+ if (member instanceof FunctionDefinitionNode) {
+ processFunctionDefinitionNode((FunctionDefinitionNode) member);
+ } else if (member instanceof ModuleVariableDeclarationNode) {
+ processModuleVariableDeclarationNode((ModuleVariableDeclarationNode) member);
+ } else if (member instanceof TypeDefinitionNode) {
+ processTypeDefinitionNode((TypeDefinitionNode) member);
+ }
}
+ for (String recordName : this.recordNamesUsedInFunction) {
+ validateRecord(ctx, this.records.get(recordName));
+ }
+ }
- if (node instanceof VariableDeclarationNode) {
- VariableDeclarationNode variableDeclarationNode = (VariableDeclarationNode) node;
- Optional initializer = variableDeclarationNode.initializer();
- if (!initializer.isEmpty()) {
- ExpressionNode expressionNode = initializer.get();
- String functionName = expressionNode.toString();
- if (functionName.contains(TO_RECORD) || functionName.contains(FROM_XML)) {
- TypedBindingPatternNode typedBindingPatternNode = variableDeclarationNode.typedBindingPattern();
- checkRecordField(typedBindingPatternNode.typeDescriptor().toString(), ctx, functionName);
+ private void processFunctionDefinitionNode(FunctionDefinitionNode functionDefinitionNode) {
+ ChildNodeList childNodeList = functionDefinitionNode.functionBody().children();
+ for (Node node : childNodeList) {
+ if (node instanceof VariableDeclarationNode) {
+ VariableDeclarationNode variableDeclarationNode = (VariableDeclarationNode) node;
+ Optional initializer = variableDeclarationNode.initializer();
+ if (initializer.isPresent()) {
+ if (isValidFunctionName(initializer.get())) {
+ addRecordName(variableDeclarationNode.typedBindingPattern().typeDescriptor());
+ }
}
}
}
- if (node instanceof ModuleVariableDeclarationNode) {
- ModuleVariableDeclarationNode moduleVariableDeclarationNode = (ModuleVariableDeclarationNode) node;
- Optional initializer = moduleVariableDeclarationNode.initializer();
- if (!initializer.isEmpty()) {
- ExpressionNode expressionNode = initializer.get();
- String functionName = expressionNode.toString();
- if (functionName.contains(TO_RECORD) || functionName.contains(FROM_XML)) {
- TypedBindingPatternNode typedBindingPatternNode =
- moduleVariableDeclarationNode.typedBindingPattern();
- checkRecordField(typedBindingPatternNode.typeDescriptor().toString(), ctx, functionName);
- }
+ }
+
+ private void processModuleVariableDeclarationNode(ModuleVariableDeclarationNode moduleVariableDeclarationNode) {
+ Optional initializer = moduleVariableDeclarationNode.initializer();
+ if (initializer.isPresent()) {
+ if (isValidFunctionName(initializer.get())) {
+ addRecordName(moduleVariableDeclarationNode.typedBindingPattern().typeDescriptor());
+ }
+ }
+ }
+
+ private boolean isValidFunctionName(ExpressionNode expressionNode) {
+ if (expressionNode instanceof CheckExpressionNode) {
+ expressionNode = ((CheckExpressionNode) expressionNode).expression();
+ }
+ if (expressionNode instanceof FunctionCallExpressionNode) {
+ FunctionCallExpressionNode functionCallExpressionNode =
+ (FunctionCallExpressionNode) expressionNode;
+ String functionName = functionCallExpressionNode.functionName().toSourceCode().trim();
+ return functionName.equals(TO_RECORD) || functionName.equals(FROM_XML);
+ }
+ return false;
+ }
+
+ private void addRecordName(TypeDescriptorNode typeDescriptor) {
+ if (typeDescriptor.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) {
+ String returnTypeName = typeDescriptor.toString().trim();
+ if (!this.recordNamesUsedInFunction.contains(returnTypeName)) {
+ this.recordNamesUsedInFunction.add(returnTypeName);
}
}
}
- private void checkRecordField(String recordName, SyntaxNodeAnalysisContext ctx, String functionName) {
- for (SyntaxNodeAnalysisContext syntaxNodeAnalysisContext: this.nodes) {
- if (!this.validatedNodes.contains(syntaxNodeAnalysisContext)) {
- RecordFieldNode recordFieldNode = ((RecordFieldNode) syntaxNodeAnalysisContext.node());
- String recordNameOfField = recordFieldNode.parent().parent().children().get(1).toString().trim();
- if (recordNameOfField.equals(recordName.trim())) {
- this.validatedNodes.add(syntaxNodeAnalysisContext);
- Optional varSymOptional = syntaxNodeAnalysisContext.semanticModel().
- symbol(syntaxNodeAnalysisContext.node());
- if (varSymOptional.isPresent()) {
- TypeSymbol typeSymbol = ((RecordFieldSymbol) varSymOptional.get()).typeDescriptor();
- if (typeSymbol instanceof UnionTypeSymbol) {
- List typeSymbols = ((UnionTypeSymbol) typeSymbol).memberTypeDescriptors();
- for (TypeSymbol symbol : typeSymbols) {
- validateType(ctx, recordFieldNode, symbol, functionName);
- }
- if (functionName.contains(FROM_XML)) {
- validateUnionType(ctx, typeSymbols, recordFieldNode, functionName);
- }
- } else {
- validateType(ctx, recordFieldNode, typeSymbol, functionName);
- }
+ private void processTypeDefinitionNode(TypeDefinitionNode typeDefinitionNode) {
+ Node typeDescriptor = typeDefinitionNode.typeDescriptor();
+ if (typeDescriptor instanceof RecordTypeDescriptorNode) {
+ RecordTypeDescriptorNode recordTypeDescriptorNode = (RecordTypeDescriptorNode) typeDescriptor;
+ Record record = new Record(typeDefinitionNode.typeName().text(), typeDefinitionNode.location());
+ typeDefinitionNode.metadata().ifPresent(metadataNode -> {
+ NodeList annotations = metadataNode.annotations();
+ for (AnnotationNode annotationNode : annotations) {
+ if (annotationNode.annotReference().toSourceCode().trim().equals(NAME_ANNOTATION)) {
+ record.setNameAnnotation();
}
}
+ });
+ for (Node field : recordTypeDescriptorNode.fields()) {
+ Node type;
+ if (field instanceof RecordFieldNode) {
+ RecordFieldNode recordFieldNode = (RecordFieldNode) field;
+ type = recordFieldNode.typeName();
+ } else {
+ RecordFieldWithDefaultValueNode recordFieldNode = (RecordFieldWithDefaultValueNode) field;
+ type = recordFieldNode.typeName();
+ }
+ processFieldType(type, record);
}
+ this.records.put(record.getName(), record);
+ }
+ }
+
+ private void processFieldType(Node type, Record record) {
+ if (type instanceof OptionalTypeDescriptorNode) {
+ record.addOptionalFieldLocations(type.location());
+ type = ((OptionalTypeDescriptorNode) type).typeDescriptor();
+ }
+ if (type instanceof NilTypeDescriptorNode) {
+ record.addOptionalFieldLocations(type.location());
+ }
+ if (type instanceof UnionTypeDescriptorNode) {
+ processUnionType((UnionTypeDescriptorNode) type, 0, record, type);
+ }
+ if (type instanceof ArrayTypeDescriptorNode) {
+ type = ((ArrayTypeDescriptorNode) type).memberTypeDesc();
+ }
+ if (type instanceof SimpleNameReferenceNode) {
+ SimpleNameReferenceNode simpleNameReferenceNode = (SimpleNameReferenceNode) type;
+ record.addChildRecordNames(simpleNameReferenceNode.name().text().trim());
}
}
- private void validateType(SyntaxNodeAnalysisContext ctx, RecordFieldNode recordFieldNode, TypeSymbol typeSymbol,
- String functionName) {
- if (typeSymbol instanceof NilTypeSymbol) {
- reportDiagnosticInfo(ctx, recordFieldNode);
- } else if (typeSymbol instanceof TypeReferenceTypeSymbol) {
- checkRecordField(typeSymbol.getName().get(), ctx, functionName);
- } else if (typeSymbol instanceof ArrayTypeSymbol) {
- TypeSymbol arrayTypeSymbol = ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor();
- if (arrayTypeSymbol instanceof TypeReferenceTypeSymbol) {
- checkRecordField(arrayTypeSymbol.getName().get(), ctx, functionName);
+ private void processUnionType(UnionTypeDescriptorNode unionTypeDescriptorNode, int noOfSimpleNamesType,
+ Record record, Node type) {
+ for (Node unionType : unionTypeDescriptorNode.children()) {
+ if (unionType instanceof UnionTypeDescriptorNode) {
+ processUnionType((UnionTypeDescriptorNode) unionType, noOfSimpleNamesType, record, type);
+ }
+ if (unionType instanceof OptionalTypeDescriptorNode) {
+ record.addOptionalFieldLocations(unionType.location());
+ unionType = ((OptionalTypeDescriptorNode) unionType).typeDescriptor();
+ }
+ if (unionType instanceof NilTypeDescriptorNode) {
+ record.addOptionalFieldLocations(unionType.location());
+ }
+ if (unionType instanceof ArrayTypeDescriptorNode) {
+ unionType = ((ArrayTypeDescriptorNode) unionType).memberTypeDesc();
+ }
+ if (unionType instanceof SimpleNameReferenceNode) {
+ noOfSimpleNamesType++;
+ SimpleNameReferenceNode simpleNameReferenceNode = (SimpleNameReferenceNode) unionType;
+ record.addChildRecordNames(simpleNameReferenceNode.name().text().trim());
}
}
+ if (noOfSimpleNamesType > 1) {
+ record.addMultipleNonPrimitiveTypeLocations(type.location());
+ }
}
- private void validateUnionType(SyntaxNodeAnalysisContext ctx, List typeSymbols,
- RecordFieldNode recordFieldNode, String functionName) {
- int noOfNonPrimitiveType = 0;
- for (TypeSymbol typeSymbol : typeSymbols) {
- if (typeSymbol instanceof TypeReferenceTypeSymbol) {
- noOfNonPrimitiveType += 1;
- checkRecordField(typeSymbol.getName().get(), ctx, functionName);
- } else if (typeSymbol instanceof ArrayTypeSymbol) {
- TypeSymbol arrayTypeSymbol = ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor();
- if (arrayTypeSymbol instanceof TypeReferenceTypeSymbol) {
- noOfNonPrimitiveType += 1;
- checkRecordField(arrayTypeSymbol.getName().get(), ctx, functionName);
+ private void validateRecord(SyntaxNodeAnalysisContext ctx, Record record) {
+ this.validatedRecords.add(record.getName());
+ for (Location location : record.getMultipleNonPrimitiveTypeLocations()) {
+ reportDiagnosticInfo(ctx, location, DiagnosticsCodes.XMLDATA_102);
+ }
+ for (Location location : record.getOptionalFieldLocations()) {
+ reportDiagnosticInfo(ctx, location, DiagnosticsCodes.XMLDATA_101);
+ }
+ for (String childRecordName : record.getChildRecordNames()) {
+ if (!this.validatedRecords.contains(childRecordName)) {
+ Record childRecord = this.records.get(childRecordName);
+ validateRecord(ctx, childRecord);
+ if (childRecord.hasNameAnnotation() && !recordNamesUsedInFunction.contains(childRecordName)) {
+ reportDiagnosticInfo(ctx, childRecord.getLocation(), DiagnosticsCodes.XMLDATA_103);
}
}
}
- if (noOfNonPrimitiveType > 1) {
- DiagnosticInfo diagnosticInfo = new DiagnosticInfo(DiagnosticsCodes.XMLDATA_102.getCode(),
- DiagnosticsCodes.XMLDATA_102.getMessage(), DiagnosticsCodes.XMLDATA_102.getSeverity());
- ctx.reportDiagnostic(
- DiagnosticFactory.createDiagnostic(diagnosticInfo, recordFieldNode.location()));
- }
}
- private void reportDiagnosticInfo(SyntaxNodeAnalysisContext ctx, RecordFieldNode recordFieldNode) {
- DiagnosticInfo diagnosticInfo = new DiagnosticInfo(DiagnosticsCodes.XMLDATA_101.getCode(),
- DiagnosticsCodes.XMLDATA_101.getMessage(), DiagnosticsCodes.XMLDATA_101.getSeverity());
- ctx.reportDiagnostic(
- DiagnosticFactory.createDiagnostic(diagnosticInfo, recordFieldNode.location()));
+ private void reportDiagnosticInfo(SyntaxNodeAnalysisContext ctx, Location location,
+ DiagnosticsCodes diagnosticsCodes) {
+ DiagnosticInfo diagnosticInfo = new DiagnosticInfo(diagnosticsCodes.getCode(),
+ diagnosticsCodes.getMessage(), diagnosticsCodes.getSeverity());
+ ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, location));
}
}
diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/object/Record.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/object/Record.java
new file mode 100644
index 00000000..ef6484d9
--- /dev/null
+++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/xmldata/compiler/object/Record.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package io.ballerina.stdlib.xmldata.compiler.object;
+
+import io.ballerina.compiler.syntax.tree.NodeLocation;
+import io.ballerina.tools.diagnostics.Location;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to store details of record.
+ *
+ * @since 2.4.1
+ */
+public class Record {
+
+ private final String name;
+ private final Location location;
+ private Boolean nameAnnotation = false;
+ private final List optionalFieldLocations = new ArrayList<>();
+ private final List childRecordNames = new ArrayList<>();
+ private final List multipleNonPrimitiveTypeLocations = new ArrayList<>();
+
+ public Record(String name, Location location) {
+ this.name = name;
+ this.location = location;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void addOptionalFieldLocations(NodeLocation optionalsField) {
+ optionalFieldLocations.add(optionalsField);
+ }
+
+ public List getOptionalFieldLocations() {
+ return optionalFieldLocations;
+ }
+
+ public void addMultipleNonPrimitiveTypeLocations(NodeLocation optionalsField) {
+ multipleNonPrimitiveTypeLocations.add(optionalsField);
+ }
+
+ public List getMultipleNonPrimitiveTypeLocations() {
+ return multipleNonPrimitiveTypeLocations;
+ }
+
+ public void addChildRecordNames(String name) {
+ if (!childRecordNames.contains(name)) {
+ childRecordNames.add(name);
+ }
+ }
+
+ public List getChildRecordNames() {
+ return childRecordNames;
+ }
+
+ public void setNameAnnotation() {
+ nameAnnotation = true;
+ }
+
+ public boolean hasNameAnnotation() {
+ return nameAnnotation;
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+}
diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/MapFromXml.java b/native/src/main/java/io/ballerina/stdlib/xmldata/MapFromXml.java
index 10281975..21c1dc93 100644
--- a/native/src/main/java/io/ballerina/stdlib/xmldata/MapFromXml.java
+++ b/native/src/main/java/io/ballerina/stdlib/xmldata/MapFromXml.java
@@ -23,6 +23,7 @@
import io.ballerina.runtime.api.creators.ValueCreator;
import io.ballerina.runtime.api.types.ArrayType;
import io.ballerina.runtime.api.types.MapType;
+import io.ballerina.runtime.api.types.RecordType;
import io.ballerina.runtime.api.types.TableType;
import io.ballerina.runtime.api.types.Type;
import io.ballerina.runtime.api.utils.StringUtils;
@@ -55,7 +56,8 @@ public static Object fromXml(BXml xml, BTypedesc type) {
Object output;
try {
if (describingType.getFlags() != Constants.DEFAULT_TYPE_FLAG) {
- String recordName = describingType.getName();
+ String recordName = getXmlNameFromRecordAnnotation((RecordType) describingType,
+ describingType.getName());
String elementName = getKey(xml);
if (!recordName.equals(elementName)) {
return XmlDataUtils.getError("The record type name: " + recordName +
@@ -150,4 +152,16 @@ private static boolean isNotValidXml(List sequence) {
// Valid XML format: VALUE
return (sequence.size() == 1 && !sequence.get(0).elements().children().isEmpty()) || sequence.size() > 1;
}
+
+ @SuppressWarnings("unchecked")
+ public static String getXmlNameFromRecordAnnotation(RecordType record, String recordName) {
+ BMap annotations = record.getAnnotations();
+ for (BString annotationsKey : annotations.getKeys()) {
+ String key = annotationsKey.getValue();
+ if (!key.contains(Constants.FIELD) && key.endsWith(Constants.NAME)) {
+ return ((BMap) annotations.get(annotationsKey)).get(Constants.VALUE).toString();
+ }
+ }
+ return recordName;
+ }
}
diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java
index 153867fd..2f7ceb49 100644
--- a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java
+++ b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java
@@ -39,6 +39,7 @@
import io.ballerina.runtime.api.values.BXmlItem;
import io.ballerina.runtime.api.values.BXmlSequence;
import io.ballerina.stdlib.xmldata.utils.Constants;
+import io.ballerina.stdlib.xmldata.utils.FieldDetails;
import io.ballerina.stdlib.xmldata.utils.XmlDataUtils;
import java.math.BigDecimal;
@@ -83,7 +84,8 @@ public static Object toJson(BXml xml, BMap, ?> options) {
String attributePrefix = ((BString) options.get(StringUtils.fromString(Constants.OPTIONS_ATTRIBUTE_PREFIX)))
.getValue();
boolean preserveNamespaces = ((Boolean) options.get(StringUtils.fromString(Constants.OPTIONS_PRESERVE_NS)));
- return convertToJSON(xml, attributePrefix, preserveNamespaces, null, null);
+ return convertToJSON(xml, attributePrefix, preserveNamespaces, null, null,
+ new FieldDetails());
} catch (Exception e) {
return XmlDataUtils.getError(e.getMessage());
}
@@ -91,7 +93,8 @@ public static Object toJson(BXml xml, BMap, ?> options) {
public static Object toJson(BXml xml, boolean preserveNamespaces, String attributePrefix, Type type) {
try {
- return convertToJSON(xml, attributePrefix, preserveNamespaces, type, null);
+ return convertToJSON(xml, attributePrefix, preserveNamespaces, type, null,
+ new FieldDetails());
} catch (Exception e) {
return XmlDataUtils.getError(e.getMessage());
}
@@ -106,7 +109,8 @@ public static Object toJson(BXml xml, boolean preserveNamespaces, String attribu
* @return JSON representation of the given xml object
*/
public static Object convertToJSON(BXml xml, String attributePrefix, boolean preserveNamespaces, Type type,
- BMap parentAttributeMap) throws Exception {
+ BMap parentAttributeMap,
+ FieldDetails fieldDetails) throws Exception {
if (type instanceof MapType) {
MapType mapType = (MapType) type;
if (mapType.getConstrainedType().getTag() == TypeTags.XML_TAG) {
@@ -116,14 +120,15 @@ public static Object convertToJSON(BXml xml, String attributePrefix, boolean pre
}
}
if (xml instanceof BXmlItem) {
- return convertElement((BXmlItem) xml, attributePrefix, preserveNamespaces, type, parentAttributeMap);
+ return convertElement((BXmlItem) xml, attributePrefix, preserveNamespaces, type, parentAttributeMap,
+ fieldDetails);
} else if (xml instanceof BXmlSequence) {
BXmlSequence xmlSequence = (BXmlSequence) xml;
if (xmlSequence.isEmpty()) {
return StringUtils.fromString(EMPTY_STRING);
}
Object seq = convertBXmlSequence(xmlSequence, attributePrefix, preserveNamespaces, type,
- parentAttributeMap);
+ parentAttributeMap, fieldDetails);
if (seq == null) {
return createNewJsonList();
}
@@ -166,15 +171,18 @@ private static Object convertValue(BXml xml, Type type) throws Exception {
@SuppressWarnings("unchecked")
private static Object convertElement(BXmlItem xmlItem, String attributePrefix,
boolean preserveNamespaces, Type type,
- BMap parentAttributeMap) throws Exception {
+ BMap parentAttributeMap,
+ FieldDetails fieldDetail) throws Exception {
BMap childrenData = createMapValue(type);
BMap attributeMap = xmlItem.getAttributesMap();
- String keyValue = getElementKey(xmlItem, preserveNamespaces);
- Type fieldType = getFieldType(keyValue, type);
+ String fieldName = getElementKey(xmlItem, preserveNamespaces);
+ processFieldDetails(fieldName, type, fieldDetail);
+ fieldName = fieldDetail.getName();
+ Type fieldType = fieldDetail.getType();
processAttributeWithAnnotation(xmlItem, attributePrefix, preserveNamespaces, childrenData, fieldType,
- attributeMap, parentAttributeMap);
+ attributeMap, parentAttributeMap, fieldDetail);
Object children = convertBXmlSequence(xmlItem.getChildrenSeq(), attributePrefix, preserveNamespaces,
- fieldType, attributeMap);
+ fieldType, attributeMap, fieldDetail);
BMap rootNode = createMapValue(type);
if (type != null && fieldType instanceof ArrayType && children instanceof BMap &&
TypeUtils.getReferredType(((ArrayType) fieldType).getElementType()) instanceof RecordType) {
@@ -183,13 +191,15 @@ private static Object convertElement(BXmlItem xmlItem, String attributePrefix,
}
children = convertToArray(fieldType, children);
}
- return insertDataToMap(childrenData, children, rootNode, keyValue, fieldType);
+ fieldDetail.setParentArrayName(fieldName);
+ return insertDataToMap(childrenData, children, rootNode, fieldName, fieldType, fieldDetail);
}
private static void processAttributeWithAnnotation(BXmlItem xmlItem, String attributePrefix,
- boolean preserveNamespaces, BMap childrenData,
- Type fieldType, BMap attributeMap,
- BMap parentAttributeMap) throws Exception {
+ boolean preserveNamespaces, BMap childrenData,
+ Type fieldType, BMap attributeMap,
+ BMap parentAttributeMap,
+ FieldDetails fieldDetails) throws Exception {
if (!attributePrefix.equals(Constants.SKIP_ATTRIBUTE)) {
BMap annotations = null;
if (attributePrefix.equals(Constants.ADD_IF_HAS_ANNOTATION)) {
@@ -210,14 +220,14 @@ private static void processAttributeWithAnnotation(BXmlItem xmlItem, String attr
}
}
processAttributes(attributeMap, attributePrefix, childrenData, fieldType,
- parentAttributeMap, xmlItem.getQName().getPrefix(), preserveNamespaces, annotations);
+ parentAttributeMap, xmlItem.getQName().getPrefix(), preserveNamespaces, annotations, fieldDetails);
}
}
@SuppressWarnings("unchecked")
private static BMap insertDataToMap(BMap childrenData, Object children,
BMap rootNode, String keyValue,
- Type fieldType) throws Exception {
+ Type fieldType, FieldDetails fieldDetails) throws Exception {
if (childrenData.size() > 0) {
if (children instanceof BMap) {
BMap data = (BMap) children;
@@ -230,7 +240,7 @@ private static BMap insertDataToMap(BMap child
} else if (children instanceof BArray) {
put(rootNode, keyValue, children);
} else if (children instanceof BString) {
- putAsFieldTypes(childrenData, CONTENT, children.toString().trim(), fieldType);
+ putAsFieldTypes(childrenData, CONTENT, children.toString().trim(), fieldType, fieldDetails);
put(rootNode, keyValue, childrenData);
return rootNode;
} else {
@@ -243,12 +253,12 @@ private static BMap insertDataToMap(BMap child
if (fieldType instanceof ReferenceType && TypeUtils.getReferredType(fieldType) instanceof RecordType) {
put(rootNode, keyValue, ValueCreator.createMapValue(Constants.JSON_MAP_TYPE));
} else {
- putAsFieldTypes(rootNode, keyValue, EMPTY_STRING, fieldType);
+ putAsFieldTypes(rootNode, keyValue, EMPTY_STRING, fieldType, fieldDetails);
}
} else if (children instanceof BArray) {
put(rootNode, keyValue, children);
} else if (children instanceof BString) {
- putAsFieldTypes(rootNode, keyValue, children.toString().trim(), fieldType);
+ putAsFieldTypes(rootNode, keyValue, children.toString().trim(), fieldType, fieldDetails);
} else {
put(rootNode, keyValue, children);
}
@@ -256,45 +266,79 @@ private static BMap insertDataToMap(BMap child
return rootNode;
}
- private static Type getFieldType(String fieldName, Type type) {
+ private static void processFieldDetails(String fieldName, Type type, FieldDetails fieldDetails) {
if (type != null) {
if (type.getTag() == TypeTags.RECORD_TYPE_TAG) {
- return getRecordFieldType(type, fieldName);
+ getRecordFieldTypeAndName(type, fieldName, fieldDetails);
} else if (type.getTag() == TypeTags.ARRAY_TAG) {
Type fieldType = TypeUtils.getReferredType(((ArrayType) type).getElementType());
if (fieldType instanceof RecordType) {
- return getRecordFieldType(fieldType, fieldName);
+ getRecordFieldTypeAndName(fieldType, fieldName, fieldDetails);
+ } else {
+ setNameAndTypeIntoFieldDetails(fieldType, fieldName, fieldDetails);
}
- return fieldType;
} else if (type.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) {
Type referredType = TypeUtils.getReferredType(type);
if (referredType.getTag() == TypeTags.RECORD_TYPE_TAG) {
- return getRecordFieldType(referredType, fieldName);
+ getRecordFieldTypeAndName(referredType, fieldName, fieldDetails);
+ } else {
+ setNameAndTypeIntoFieldDetails(type, fieldName, fieldDetails);
}
} else if (type.getTag() == TypeTags.MAP_TAG) {
Type valueType = ((MapType) type).getConstrainedType();
if (valueType.getTag() == TypeTags.TABLE_TAG) {
- return ((TableType) valueType).getConstrainedType();
+ setNameAndTypeIntoFieldDetails(((TableType) valueType).getConstrainedType(),
+ fieldName, fieldDetails);
+ } else {
+ setNameAndTypeIntoFieldDetails(valueType, fieldName, fieldDetails);
}
- return valueType;
+ } else {
+ setNameAndTypeIntoFieldDetails(type, fieldName, fieldDetails);
}
+ } else {
+ setNameAndTypeIntoFieldDetails(null, fieldName, fieldDetails);
}
- return type;
}
- private static Type getRecordFieldType(Type type, String fieldName) {
+ @SuppressWarnings("unchecked")
+ private static void getRecordFieldTypeAndName(Type type, String fieldName, FieldDetails fieldDetails) {
+ setNameAndTypeIntoFieldDetails(type, fieldName, fieldDetails);
RecordType recordType = (RecordType) type;
Map fields = recordType.getFields();
+ BMap annotations = recordType.getAnnotations();
+ for (BString annotationsKey : annotations.getKeys()) {
+ if (!annotationsKey.getValue().contains(Constants.FIELD)) {
+ continue;
+ }
+ BMap annotationsForField = (BMap) annotations.get(annotationsKey);
+ for (BString annotationForField : annotationsForField.getKeys()) {
+ if (!annotationForField.getValue().endsWith(Constants.NAME)) {
+ continue;
+ }
+ BMap value = (BMap) annotationsForField.get(annotationForField);
+ for (Map.Entry entry : value.entrySet()) {
+ if (entry.getValue().toString().trim().equals(fieldName)) {
+ fieldName = annotationsKey.getValue().split("\\.")[1];
+ fieldDetails.setName(fieldName);
+ }
+ }
+ }
+ }
if (fields.get(fieldName) != null) {
- return fields.get(fieldName).getFieldType();
+ fieldDetails.setType(fields.get(fieldName).getFieldType());
}
- return type;
+ }
+
+ private static void setNameAndTypeIntoFieldDetails(Type type, String fieldName, FieldDetails fieldDetails) {
+ fieldDetails.setName(fieldName);
+ fieldDetails.setType(type);
}
private static void processAttributes(BMap attributeMap, String attributePrefix,
BMap mapData, Type type,
BMap parentAttributeMap, String prefix,
- boolean preserveNamespaces, BMap annotations)
+ boolean preserveNamespaces, BMap annotations,
+ FieldDetails fieldDetails)
throws Exception {
Map nsPrefixMap = getNamespacePrefixes(attributeMap);
if (prefix != null && preserveNamespaces && parentAttributeMap != null) {
@@ -303,8 +347,9 @@ private static void processAttributes(BMap attributeMap, Strin
if (!isNamespacePrefixEntry(entry) ||
!isBelongingToElement(parentAttributeMap, entry.getKey(), value)) {
String key = getAttributeKey(attributePrefix, getKey(entry, nsPrefixMap, preserveNamespaces));
- checkAnnotationAndAddAttributes(annotations, mapData, getFieldType(key, type), key,
- value.getValue(), attributePrefix);
+ processFieldDetails(key, type, fieldDetails);
+ checkAnnotationAndAddAttributes(annotations, mapData, fieldDetails.getType(),
+ fieldDetails.getName(), value.getValue(), attributePrefix, fieldDetails);
}
}
} else {
@@ -312,8 +357,9 @@ private static void processAttributes(BMap attributeMap, Strin
String key = getKey(entry, nsPrefixMap, preserveNamespaces);
if (key != null) {
key = getAttributeKey(attributePrefix, key);
- checkAnnotationAndAddAttributes(annotations, mapData, getFieldType(key, type), key,
- entry.getValue().getValue(), attributePrefix);
+ processFieldDetails(key, type, fieldDetails);
+ checkAnnotationAndAddAttributes(annotations, mapData, fieldDetails.getType(),
+ fieldDetails.getName(), entry.getValue().getValue(), attributePrefix, fieldDetails);
}
}
}
@@ -330,9 +376,10 @@ private static String getAttributeKey(String attributePrefix , String key) {
@SuppressWarnings("unchecked")
private static void checkAnnotationAndAddAttributes(BMap annotations,
BMap mapData, Type type, String key,
- String value, String attributePrefix) throws Exception {
+ String value, String attributePrefix,
+ FieldDetails fieldDetails) throws Exception {
if (!attributePrefix.equals(Constants.ADD_IF_HAS_ANNOTATION)) {
- putAsFieldTypes(mapData, key, value, type);
+ putAsFieldTypes(mapData, key, value, type, fieldDetails);
} else if (annotations != null && annotations.size() > 0) {
BString annotationKey = StringUtils.fromString((Constants.FIELD + key).replace(":",
"\\:"));
@@ -342,7 +389,9 @@ private static void checkAnnotationAndAddAttributes(BMap annota
BMap annotationsForField = (BMap) annotations.get(annotationsKey);
for (BString annotationForField : annotationsForField.getKeys()) {
if (annotationForField.getValue().endsWith(Constants.ATTRIBUTE)) {
- putAsFieldTypes(mapData, key, value, getFieldType(key, type));
+ processFieldDetails(key, type, fieldDetails);
+ putAsFieldTypes(mapData, fieldDetails.getName(), value, fieldDetails.getType(),
+ fieldDetails);
break;
}
}
@@ -367,18 +416,20 @@ private static void checkAnnotationAndAddAttributes(BMap annota
}
private static boolean isBelongingToElement(BMap parentAttributeMap, BString key,
- BString value) {
+ BString value) {
return parentAttributeMap.containsKey(key) && parentAttributeMap.get(key).getValue().equals(value.getValue());
}
- private static void putAsFieldTypes(BMap map, String key, String value, Type type)
- throws Exception {
+ private static void putAsFieldTypes(BMap map, String key, String value, Type type,
+ FieldDetails fieldDetails) throws Exception {
if (type != null) {
if (type instanceof ArrayType) {
Type fieldType = TypeUtils.getReferredType(((ArrayType) type).getElementType());
if (fieldType instanceof RecordType) {
if (((RecordType) fieldType).getFields().get(key) != null) {
- type = ((RecordType) fieldType).getFields().get(key).getFieldType();
+ getRecordFieldTypeAndName(fieldType, key, fieldDetails);
+ key = fieldDetails.getName();
+ type = fieldDetails.getType();
}
}
}
@@ -503,7 +554,8 @@ private static BArray convertToArray(Type valueType, Object value) throws Except
*/
private static Object convertBXmlSequence(BXmlSequence xmlSequence, String attributePrefix,
boolean preserveNamespaces, Type type,
- BMap parentAttributeMap) throws Exception {
+ BMap parentAttributeMap,
+ FieldDetails fieldDetails) throws Exception {
List sequence = xmlSequence.getChildrenList();
List newSequence = new ArrayList<>();
for (BXml value: sequence) {
@@ -522,14 +574,16 @@ private static Object convertBXmlSequence(BXmlSequence xmlSequence, String attri
return xmlSequence.elements();
}
return convertHeterogeneousSequence(attributePrefix, preserveNamespaces, newSequence, type,
- parentAttributeMap);
+ parentAttributeMap, fieldDetails);
}
private static Object convertHeterogeneousSequence(String attributePrefix, boolean preserveNamespaces,
List sequence, Type type,
- BMap parentAttributeMap) throws Exception {
+ BMap parentAttributeMap,
+ FieldDetails fieldDetails) throws Exception {
if (sequence.size() == 1 && !(type != null && type.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG)) {
- return convertToJSON(sequence.get(0), attributePrefix, preserveNamespaces, type, parentAttributeMap);
+ return convertToJSON(sequence.get(0), attributePrefix, preserveNamespaces, type,
+ parentAttributeMap, fieldDetails);
}
BMap mapJson = createMapValue(type);
for (BXml bxml : sequence) {
@@ -551,9 +605,10 @@ private static Object convertHeterogeneousSequence(String attributePrefix, boole
mapJson.put(fromString(CONTENT), fromString(bxml.toString().trim()));
}
} else {
- BString elementName = fromString(getElementKey((BXmlItem) bxml, preserveNamespaces));
- Object result = convertToJSON(bxml, attributePrefix, preserveNamespaces, type, parentAttributeMap);
- result = validateResult(result, elementName);
+ Object processingData = convertToJSON(bxml, attributePrefix, preserveNamespaces, type,
+ parentAttributeMap, fieldDetails);
+ BString elementName = StringUtils.fromString(fieldDetails.getParentArrayName());
+ Object result = validateResult(processingData, elementName);
Object value = mapJson.get(elementName);
if (value == null) {
mapJson.put(elementName, result);
diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/Constants.java
index df3862ad..e35f35ea 100644
--- a/native/src/main/java/io/ballerina/stdlib/xmldata/utils/Constants.java
+++ b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/Constants.java
@@ -22,6 +22,8 @@
import io.ballerina.runtime.api.creators.TypeCreator;
import io.ballerina.runtime.api.types.ArrayType;
import io.ballerina.runtime.api.types.MapType;
+import io.ballerina.runtime.api.utils.StringUtils;
+import io.ballerina.runtime.api.values.BString;
/**
* Constants used in Ballerina XmlData library.
@@ -46,4 +48,6 @@ private Constants() {}
public static final String SKIP_ATTRIBUTE = "skip";
public static final String ADD_IF_HAS_ANNOTATION = "add";
public static final int DEFAULT_TYPE_FLAG = 2049;
+ public static final String NAME = "Name";
+ public static final BString VALUE = StringUtils.fromString("value");
}
diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/utils/FieldDetails.java b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/FieldDetails.java
new file mode 100644
index 00000000..ba90b6cd
--- /dev/null
+++ b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/FieldDetails.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package io.ballerina.stdlib.xmldata.utils;
+
+import io.ballerina.runtime.api.types.Type;
+
+/**
+ * Class to store field details.
+ *
+ * @since 2.6.1
+ */
+public class FieldDetails {
+ private String name;
+ private String parentArrayName;
+ private Type type;
+
+ public FieldDetails() {}
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Type getType() {
+ return this.type;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ public void setParentArrayName(String parentArrayName) {
+ this.parentArrayName = parentArrayName;
+ }
+
+ public String getParentArrayName() {
+ return this.parentArrayName;
+ }
+}
diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/utils/XmlDataUtils.java b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/XmlDataUtils.java
index 95508fb5..92206e96 100644
--- a/native/src/main/java/io/ballerina/stdlib/xmldata/utils/XmlDataUtils.java
+++ b/native/src/main/java/io/ballerina/stdlib/xmldata/utils/XmlDataUtils.java
@@ -41,9 +41,6 @@
import java.util.Locale;
import java.util.Map;
-import static io.ballerina.stdlib.xmldata.utils.Constants.COLON;
-import static io.ballerina.stdlib.xmldata.utils.Constants.UNDERSCORE;
-
/**
* A util class for the XmlData package's native implementation.
*
@@ -52,7 +49,6 @@
public class XmlDataUtils {
private static final String ERROR = "Error";
- private static final String NAME = "Name";
private static final String ATTRIBUTE_PREFIX = "attribute_";
private static final String VALUE = "value";
@@ -115,7 +111,7 @@ private static BMap addFields(BMap input, Type
} else if (fieldType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) {
Type referredType = TypeUtils.getReferredType(fieldType);
if (annotations.size() > 0) {
- key = getKeyNameFromAnnotation(annotations, key);
+ key = getKeyNameFromAnnotation(annotations, key);
}
BMap subRecordAnnotations = ((RecordType) referredType).getAnnotations();
key = getElementName(subRecordAnnotations, key);
@@ -163,7 +159,7 @@ private static void processRecord(String key, BMap parentAnnota
private static void addPrimitiveValue(BString key, BMap annotations,
BMap record, Object value) {
BString annotationKey =
- StringUtils.fromString((Constants.FIELD + key).replace(":", "\\:"));
+ StringUtils.fromString((Constants.FIELD + key).replace(Constants.COLON, "\\:"));
if (annotations.containsKey(annotationKey)) {
BMap annotationValue = (BMap) annotations.get(annotationKey);
record.put(StringUtils.fromString(processFieldAnnotation(annotationValue, key.getValue())), value);
@@ -212,7 +208,7 @@ private static void processArray(Type childType, BMap annotatio
@SuppressWarnings("unchecked")
private static String getKeyNameFromAnnotation(BMap annotations, String keyName) {
BString annotationKey = StringUtils.fromString((Constants.FIELD + keyName).
- replace(":", "\\:"));
+ replace(Constants.COLON, "\\:"));
if (annotations.containsKey(annotationKey)) {
BMap annotationValue = (BMap) annotations.get(annotationKey);
return processFieldAnnotation(annotationValue, keyName);
@@ -251,13 +247,13 @@ private static BMap processParentAnnotation(Type type, BMap annotation, String key) {
for (BString value : annotation.getKeys()) {
String stringValue = value.getValue();
- if (stringValue.endsWith(NAME)) {
+ if (stringValue.endsWith(Constants.NAME)) {
BMap names = (BMap) annotation.get(value);
String name = names.get(StringUtils.fromString(VALUE)).toString();
- if (key.contains(COLON)) {
- key = key.substring(0, key.indexOf(COLON) + 1) + name;
+ if (key.contains(Constants.COLON)) {
+ key = key.substring(0, key.indexOf(Constants.COLON) + 1) + name;
} else if (key.contains(ATTRIBUTE_PREFIX)) {
- key = key.substring(0, key.indexOf(UNDERSCORE) + 1) + name;
+ key = key.substring(0, key.indexOf(Constants.UNDERSCORE) + 1) + name;
} else {
key = name;
}
@@ -275,7 +271,7 @@ private static BString processAnnotation(BMap annotation, Strin
for (BString value : annotation.getKeys()) {
if (!value.getValue().contains(Constants.FIELD)) {
String stringValue = value.getValue();
- if (stringValue.endsWith(NAME)) {
+ if (stringValue.endsWith(Constants.NAME)) {
key = processNameAnnotation(annotation, key, value, hasNamespaceAnnotation);
}
if (stringValue.endsWith(Constants.NAME_SPACE)) {
@@ -307,10 +303,10 @@ private static String getElementName(BMap annotation, String ke
BMap namespaceAnnotation = (BMap) annotation.get(value);
BString prefix = (BString) namespaceAnnotation.get(StringUtils.fromString(Constants.PREFIX));
if (prefix != null) {
- key = prefix.getValue().concat(":").concat(key);
+ key = prefix.getValue().concat(Constants.COLON).concat(key);
}
}
- if (value.getValue().endsWith(NAME)) {
+ if (value.getValue().endsWith(Constants.NAME)) {
key = processNameAnnotation(annotation, key, value, hasNamespaceAnnotation);
}
}
@@ -323,7 +319,7 @@ private static String processNameAnnotation(BMap annotation, St
String nameValue = ((BMap) annotation.get(value)).
get(StringUtils.fromString(VALUE)).toString();
if (hasNamespaceAnnotation) {
- return key.substring(0, key.indexOf(":") + 1) + nameValue;
+ return key.substring(0, key.indexOf(Constants.COLON) + 1) + nameValue;
} else {
return nameValue;
}
@@ -339,7 +335,7 @@ private static String processNamespaceAnnotation(BMap annotatio
subRecord.put(StringUtils.fromString(ATTRIBUTE_PREFIX + "xmlns"), uri);
} else {
subRecord.put(StringUtils.fromString(ATTRIBUTE_PREFIX + "xmlns:" + prefix), uri);
- key = prefix.getValue().concat(":").concat(key);
+ key = prefix.getValue().concat(Constants.COLON).concat(key);
}
return key;
}