Olingo分析和實(shí)踐之EDM 輔助序列化器詳解(最佳實(shí)踐)
概念與定義
什么是 EDM 輔助序列化器?
EDM 輔助序列化器(EdmAssistedSerializer)是 Apache Olingo OData 框架中的一種特殊序列化器,專門(mén)設(shè)計(jì)用于在缺少完整 EDM(實(shí)體數(shù)據(jù)模型)信息的情況下進(jìn)行數(shù)據(jù)序列化。
核心概念
- EDM(Entity Data Model): OData 服務(wù)的元數(shù)據(jù)模型,定義了實(shí)體類型、屬性、關(guān)系等
- 輔助(Assisted): 表示該序列化器可以在沒(méi)有完整 EDM 信息的情況下工作
- 智能推斷: 能夠根據(jù)數(shù)據(jù)本身推斷出類型和結(jié)構(gòu)信息
設(shè)計(jì)目標(biāo)
核心特點(diǎn)
1. EDM 信息可選
// 可以在沒(méi)有 EDM 信息的情況下工作 EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON); // 如果有 EDM 信息,會(huì)利用它進(jìn)行驗(yàn)證和優(yōu)化 SerializerResult result = serializer.entityCollection( metadata, // 可選的 ServiceMetadata null, // 可選的 EdmEntityType entityCollection, // 必需的數(shù)據(jù) options // 序列化選項(xiàng) );
2. 智能類型推斷
// 自動(dòng)推斷數(shù)據(jù)類型 Entity entity = new Entity(); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "John")); entity.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 25)); entity.addProperty(new Property(null, "Salary", ValueType.PRIMITIVE, 50000.50)); entity.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, true)); // 序列化器會(huì)自動(dòng)推斷: // Name -> String // Age -> Integer // Salary -> Double // IsActive -> Boolean
3. 版本感知
// 支持不同 OData 版本 List<String> versions = Arrays.asList("4.01", "4.0"); EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON, versions); // 根據(jù)版本自動(dòng)選擇合適的常量和行為 // v4.0: 使用 Constantsv00 // v4.01+: 使用 Constantsv01
4. 元數(shù)據(jù)級(jí)別控制
// 不同的元數(shù)據(jù)級(jí)別 EdmAssistedSerializer noMetadata = odata.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA); EdmAssistedSerializer minimalMetadata = odata.createEdmAssistedSerializer(ContentType.JSON); EdmAssistedSerializer fullMetadata = odata.createEdmAssistedSerializer(ContentType.JSON_FULL_METADATA);
與標(biāo)準(zhǔn)序列化器的區(qū)別
對(duì)比表格
特性 | 標(biāo)準(zhǔn)序列化器 (ODataSerializer) | EDM 輔助序列化器 (EdmAssistedSerializer) |
---|---|---|
EDM 依賴 | 必須有完整的 EDM 信息 | EDM 信息可選,可以沒(méi)有 |
類型安全 | 編譯時(shí)類型檢查 | 運(yùn)行時(shí)類型推斷 |
性能 | 更高(有完整類型信息) | 略低(需要推斷類型) |
靈活性 | 較低,結(jié)構(gòu)固定 | 更高,支持動(dòng)態(tài)結(jié)構(gòu) |
使用場(chǎng)景 | 完整的 OData 服務(wù) | 輕量級(jí)或動(dòng)態(tài)數(shù)據(jù)序列化 |
支持格式 | JSON, XML | 僅 JSON |
開(kāi)發(fā)速度 | 需要先定義 EDM | 可以直接開(kāi)始開(kāi)發(fā) |
適用階段 | 生產(chǎn)環(huán)境 | 開(kāi)發(fā)、原型、集成階段 |
使用決策流程
工作原理
序列化流程
類型推斷機(jī)制
// 類型推斷示例 public class TypeInferenceExample { public void demonstrateTypeInference() { Entity entity = new Entity(); // 字符串類型推斷 entity.addProperty(new Property(null, "stringProp", ValueType.PRIMITIVE, "Hello")); // 輸出: "stringProp": "Hello" // 數(shù)值類型推斷 entity.addProperty(new Property(null, "intProp", ValueType.PRIMITIVE, 42)); // 輸出: "intProp@odata.type": "#Int32", "intProp": 42 entity.addProperty(new Property(null, "doubleProp", ValueType.PRIMITIVE, 3.14)); // 輸出: "doubleProp@odata.type": "#Double", "doubleProp": 3.14 // 布爾類型推斷 entity.addProperty(new Property(null, "boolProp", ValueType.PRIMITIVE, true)); // 輸出: "boolProp": true // 日期類型推斷 entity.addProperty(new Property(null, "dateProp", ValueType.PRIMITIVE, Calendar.getInstance())); // 輸出: "dateProp@odata.type": "#DateTimeOffset", "dateProp": "2025-01-15T10:30:00Z" // 復(fù)雜類型推斷 ComplexValue address = new ComplexValue(); address.getValue().add(new Property(null, "Street", ValueType.PRIMITIVE, "Main St")); address.getValue().add(new Property(null, "City", ValueType.PRIMITIVE, "Seattle")); entity.addProperty(new Property(null, "Address", ValueType.COMPLEX, address)); // 輸出: "Address": { "Street": "Main St", "City": "Seattle" } } }
詳細(xì)API分析
核心接口
public interface EdmAssistedSerializer { /** * 序列化實(shí)體集合 * @param metadata 服務(wù)元數(shù)據(jù)(可選) * @param referencedEntityType 引用的實(shí)體類型(可選) * @param entityCollection 要序列化的實(shí)體集合 * @param options 序列化選項(xiàng) * @return 序列化結(jié)果 */ SerializerResult entityCollection( ServiceMetadata metadata, EdmEntityType referencedEntityType, AbstractEntityCollection entityCollection, EdmAssistedSerializerOptions options ) throws SerializerException; }
實(shí)現(xiàn)類分析
public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { // 關(guān)鍵字段 private final boolean isIEEE754Compatible; // IEEE754 兼容性 private final boolean isODataMetadataNone; // 無(wú)元數(shù)據(jù)模式 private final boolean isODataMetadataFull; // 完整元數(shù)據(jù)模式 private final IConstants constants; // 版本常量 // 構(gòu)造函數(shù) public EdmAssistedJsonSerializer(final ContentType contentType) { this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType); this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType); this.constants = new Constantsv00(); } // 版本感知構(gòu)造函數(shù) public EdmAssistedJsonSerializer(final ContentType contentType, final IConstants constants) { this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType); this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType); this.constants = constants; } }
序列化選項(xiàng)
public class EdmAssistedSerializerOptions { private ContextURL contextURL; public static Builder with() { return new Builder(); } public static final class Builder { private final EdmAssistedSerializerOptions options; private Builder() { options = new EdmAssistedSerializerOptions(); } public Builder contextURL(final ContextURL contextURL) { options.contextURL = contextURL; return this; } public EdmAssistedSerializerOptions build() { return options; } } }
使用場(chǎng)景
1. 快速原型開(kāi)發(fā)
場(chǎng)景描述: 在項(xiàng)目初期,需要快速驗(yàn)證 OData 接口設(shè)計(jì),但還沒(méi)有完整的 EDM 模型。
@RestController @RequestMapping("/api/prototype") public class PrototypeController { private final OData odata = OData.newInstance(); @GetMapping("/users") public ResponseEntity<String> getUsers() throws SerializerException, IOException { // 快速創(chuàng)建測(cè)試數(shù)據(jù),無(wú)需預(yù)定義 EDM EntityCollection users = new EntityCollection(); // 用戶1 Entity user1 = new Entity(); user1.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, 1)); user1.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Alice")); user1.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, "alice@example.com")); user1.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 28)); user1.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, true)); users.getEntities().add(user1); // 用戶2 Entity user2 = new Entity(); user2.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, 2)); user2.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Bob")); user2.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, "bob@example.com")); user2.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 35)); user2.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, false)); users.getEntities().add(user2); // 使用 EDM 輔助序列化器 EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.JSON_FULL_METADATA); ContextURL contextURL = ContextURL.with() .entitySet("Users") .selectList("Id,Name,Email,Age,IsActive") .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); SerializerResult result = serializer.entityCollection( null, null, users, options); return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/json")) .body(IOUtils.toString(result.getContent(), StandardCharsets.UTF_8)); } }
輸出結(jié)果:
{ "@odata.context": "$metadata#Users(Id,Name,Email,Age,IsActive)", "value": [ { "@odata.id": null, "Id@odata.type": "#Int32", "Id": 1, "Name": "Alice", "Email": "alice@example.com", "Age@odata.type": "#Int32", "Age": 28, "IsActive": true }, { "@odata.id": null, "Id@odata.type": "#Int32", "Id": 2, "Name": "Bob", "Email": "bob@example.com", "Age@odata.type": "#Int32", "Age": 35, "IsActive": false } ] }
2. 動(dòng)態(tài)數(shù)據(jù)源集成
場(chǎng)景描述: 從外部數(shù)據(jù)庫(kù)或 API 動(dòng)態(tài)獲取數(shù)據(jù),數(shù)據(jù)結(jié)構(gòu)可能會(huì)變化。
@Service public class DynamicDataService { private final OData odata = OData.newInstance(); private final JdbcTemplate jdbcTemplate; public DynamicDataService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /** * 動(dòng)態(tài)查詢?nèi)我獗砀駭?shù)據(jù)并序列化為 OData 格式 */ public String queryTableAsOData(String tableName, List<String> columns) throws SerializerException, IOException { // 構(gòu)建動(dòng)態(tài) SQL String sql = "SELECT " + String.join(", ", columns) + " FROM " + tableName; // 執(zhí)行查詢 List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql); // 轉(zhuǎn)換為 OData 實(shí)體集合 EntityCollection entities = new EntityCollection(); for (Map<String, Object> row : rows) { Entity entity = new Entity(); for (Map.Entry<String, Object> entry : row.entrySet()) { String columnName = entry.getKey(); Object value = entry.getValue(); // 動(dòng)態(tài)確定值類型 ValueType valueType = determineValueType(value); entity.addProperty(new Property(null, columnName, valueType, value)); } entities.getEntities().add(entity); } // 使用 EDM 輔助序列化器 EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); ContextURL contextURL = ContextURL.with() .entitySet(tableName) .selectList(String.join(",", columns)) .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); SerializerResult result = serializer.entityCollection( null, null, entities, options); return IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); } private ValueType determineValueType(Object value) { if (value == null) return ValueType.PRIMITIVE; if (value instanceof String) return ValueType.PRIMITIVE; if (value instanceof Number) return ValueType.PRIMITIVE; if (value instanceof Boolean) return ValueType.PRIMITIVE; if (value instanceof Date || value instanceof Calendar) return ValueType.PRIMITIVE; if (value instanceof Map) return ValueType.COMPLEX; if (value instanceof Collection) return ValueType.COLLECTION_PRIMITIVE; return ValueType.PRIMITIVE; } }
3. 數(shù)據(jù)轉(zhuǎn)換管道
場(chǎng)景描述: 在數(shù)據(jù)集成管道中,需要將不同格式的數(shù)據(jù)統(tǒng)一轉(zhuǎn)換為 OData 格式。
@Component public class DataTransformationPipeline { private final OData odata = OData.newInstance(); /** * 將 CSV 數(shù)據(jù)轉(zhuǎn)換為 OData JSON 格式 */ public String transformCsvToOData(String csvContent, String entitySetName) throws SerializerException, IOException { String[] lines = csvContent.split("\n"); if (lines.length < 2) { throw new IllegalArgumentException("CSV must have at least header and one data row"); } // 解析表頭 String[] headers = lines[0].split(","); EntityCollection entities = new EntityCollection(); // 解析數(shù)據(jù)行 for (int i = 1; i < lines.length; i++) { String[] values = lines[i].split(","); Entity entity = new Entity(); for (int j = 0; j < headers.length && j < values.length; j++) { String header = headers[j].trim(); String value = values[j].trim(); // 嘗試推斷類型并轉(zhuǎn)換 Object typedValue = parseValue(value); entity.addProperty(new Property(null, header, ValueType.PRIMITIVE, typedValue)); } entities.getEntities().add(entity); } return serializeToOData(entities, entitySetName); } /** * 將 JSON 數(shù)組轉(zhuǎn)換為 OData 格式 */ public String transformJsonArrayToOData(String jsonArray, String entitySetName) throws SerializerException, IOException { ObjectMapper mapper = new ObjectMapper(); try { List<Map<String, Object>> dataList = mapper.readValue(jsonArray, new TypeReference<List<Map<String, Object>>>() {}); EntityCollection entities = new EntityCollection(); for (Map<String, Object> dataMap : dataList) { Entity entity = new Entity(); for (Map.Entry<String, Object> entry : dataMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); ValueType valueType = determineValueType(value); entity.addProperty(new Property(null, key, valueType, value)); } entities.getEntities().add(entity); } return serializeToOData(entities, entitySetName); } catch (Exception e) { throw new RuntimeException("Failed to parse JSON array", e); } } private String serializeToOData(EntityCollection entities, String entitySetName) throws SerializerException, IOException { // 支持多版本 List<String> versions = Arrays.asList("4.01", "4.0"); EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON, versions); ContextURL contextURL = ContextURL.with() .entitySet(entitySetName) .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); SerializerResult result = serializer.entityCollection( null, null, entities, options); return IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); } private Object parseValue(String value) { // 嘗試解析為不同類型 if (value.isEmpty() || "null".equalsIgnoreCase(value)) { return null; } // 布爾值 if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) { return Boolean.parseBoolean(value); } // 整數(shù) try { return Integer.parseInt(value); } catch (NumberFormatException e) { // 不是整數(shù),繼續(xù)嘗試其他類型 } // 浮點(diǎn)數(shù) try { return Double.parseDouble(value); } catch (NumberFormatException e) { // 不是浮點(diǎn)數(shù),繼續(xù)嘗試其他類型 } // 日期(簡(jiǎn)單格式) try { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); return dateFormat.parse(value); } catch (ParseException e) { // 不是日期格式 } // 默認(rèn)作為字符串 return value; } private ValueType determineValueType(Object value) { if (value == null) return ValueType.PRIMITIVE; if (value instanceof String) return ValueType.PRIMITIVE; if (value instanceof Number) return ValueType.PRIMITIVE; if (value instanceof Boolean) return ValueType.PRIMITIVE; if (value instanceof Date) return ValueType.PRIMITIVE; if (value instanceof Map) return ValueType.COMPLEX; if (value instanceof List) return ValueType.COLLECTION_PRIMITIVE; return ValueType.PRIMITIVE; } }
4. 微服務(wù)數(shù)據(jù)聚合
場(chǎng)景描述: 從多個(gè)微服務(wù)聚合數(shù)據(jù),各服務(wù)的數(shù)據(jù)格式可能不同。
@RestController @RequestMapping("/api/aggregation") public class DataAggregationController { private final OData odata = OData.newInstance(); @Autowired private UserService userService; @Autowired private OrderService orderService; @Autowired private ProductService productService; /** * 聚合用戶、訂單和產(chǎn)品數(shù)據(jù) */ @GetMapping("/dashboard") public ResponseEntity<String> getDashboardData() throws SerializerException, IOException { EntityCollection dashboardData = new EntityCollection(); // 聚合用戶數(shù)據(jù) List<User> users = userService.getActiveUsers(); for (User user : users) { Entity userEntity = new Entity(); userEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "User")); userEntity.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, user.getId())); userEntity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, user.getName())); userEntity.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, user.getEmail())); userEntity.addProperty(new Property(null, "LastLogin", ValueType.PRIMITIVE, user.getLastLogin())); // 動(dòng)態(tài)添加用戶統(tǒng)計(jì)信息 Map<String, Object> stats = userService.getUserStats(user.getId()); for (Map.Entry<String, Object> stat : stats.entrySet()) { userEntity.addProperty(new Property(null, "Stats_" + stat.getKey(), ValueType.PRIMITIVE, stat.getValue())); } dashboardData.getEntities().add(userEntity); } // 聚合訂單數(shù)據(jù) List<Order> recentOrders = orderService.getRecentOrders(30); for (Order order : recentOrders) { Entity orderEntity = new Entity(); orderEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "Order")); orderEntity.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, order.getId())); orderEntity.addProperty(new Property(null, "UserId", ValueType.PRIMITIVE, order.getUserId())); orderEntity.addProperty(new Property(null, "Amount", ValueType.PRIMITIVE, order.getAmount())); orderEntity.addProperty(new Property(null, "Status", ValueType.PRIMITIVE, order.getStatus())); orderEntity.addProperty(new Property(null, "CreatedAt", ValueType.PRIMITIVE, order.getCreatedAt())); dashboardData.getEntities().add(orderEntity); } // 聚合產(chǎn)品數(shù)據(jù)(動(dòng)態(tài)屬性) List<Map<String, Object>> productData = productService.getProductAnalytics(); for (Map<String, Object> product : productData) { Entity productEntity = new Entity(); productEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "Product")); // 動(dòng)態(tài)添加所有產(chǎn)品屬性 for (Map.Entry<String, Object> entry : product.entrySet()) { ValueType valueType = determineValueType(entry.getValue()); productEntity.addProperty(new Property(null, entry.getKey(), valueType, entry.getValue())); } dashboardData.getEntities().add(productEntity); } // 使用 EDM 輔助序列化器序列化混合數(shù)據(jù) EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); ContextURL contextURL = ContextURL.with() .entitySet("DashboardData") .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); SerializerResult result = serializer.entityCollection( null, null, dashboardData, options); String jsonOutput = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(jsonOutput); } private ValueType determineValueType(Object value) { if (value == null) return ValueType.PRIMITIVE; if (value instanceof String) return ValueType.PRIMITIVE; if (value instanceof Number) return ValueType.PRIMITIVE; if (value instanceof Boolean) return ValueType.PRIMITIVE; if (value instanceof Date || value instanceof Calendar) return ValueType.PRIMITIVE; if (value instanceof Map) return ValueType.COMPLEX; if (value instanceof Collection) return ValueType.COLLECTION_PRIMITIVE; return ValueType.PRIMITIVE; } }
代碼案例
案例1: 基礎(chǔ)使用
public class BasicUsageExample { public void basicExample() throws SerializerException, IOException { OData odata = OData.newInstance(); // 1. 創(chuàng)建 EDM 輔助序列化器 EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); // 2. 創(chuàng)建數(shù)據(jù) Entity person = new Entity(); person.addProperty(new Property(null, "FirstName", ValueType.PRIMITIVE, "John")); person.addProperty(new Property(null, "LastName", ValueType.PRIMITIVE, "Doe")); person.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 30)); EntityCollection people = new EntityCollection(); people.getEntities().add(person); // 3. 序列化 SerializerResult result = serializer.entityCollection( null, null, people, null); // 4. 獲取結(jié)果 String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); System.out.println(json); } }
案例2: 復(fù)雜類型處理
public class ComplexTypeExample { public void complexTypeExample() throws SerializerException, IOException { OData odata = OData.newInstance(); EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.JSON_FULL_METADATA); // 創(chuàng)建包含復(fù)雜類型的實(shí)體 Entity employee = new Entity(); employee.addProperty(new Property(null, "EmployeeId", ValueType.PRIMITIVE, 1001)); employee.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Alice Johnson")); // 創(chuàng)建地址復(fù)雜類型 ComplexValue address = new ComplexValue(); address.getValue().add(new Property(null, "Street", ValueType.PRIMITIVE, "123 Main St")); address.getValue().add(new Property(null, "City", ValueType.PRIMITIVE, "Seattle")); address.getValue().add(new Property(null, "State", ValueType.PRIMITIVE, "WA")); address.getValue().add(new Property(null, "ZipCode", ValueType.PRIMITIVE, "98101")); employee.addProperty(new Property(null, "Address", ValueType.COMPLEX, address)); // 創(chuàng)建聯(lián)系方式復(fù)雜類型 ComplexValue contact = new ComplexValue(); contact.getValue().add(new Property(null, "Email", ValueType.PRIMITIVE, "alice@company.com")); contact.getValue().add(new Property(null, "Phone", ValueType.PRIMITIVE, "+1-555-0123")); employee.addProperty(new Property(null, "Contact", ValueType.COMPLEX, contact)); // 創(chuàng)建技能集合 List<String> skills = Arrays.asList("Java", "Spring", "OData", "SQL"); employee.addProperty(new Property(null, "Skills", ValueType.COLLECTION_PRIMITIVE, skills)); EntityCollection employees = new EntityCollection(); employees.getEntities().add(employee); // 設(shè)置上下文 URL ContextURL contextURL = ContextURL.with() .entitySet("Employees") .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); SerializerResult result = serializer.entityCollection( null, null, employees, options); String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); System.out.println("Complex Type Example Output:"); System.out.println(json); } }
輸出結(jié)果:
{ "@odata.context": "$metadata#Employees", "value": [ { "@odata.id": null, "EmployeeId@odata.type": "#Int32", "EmployeeId": 1001, "Name": "Alice Johnson", "Address": { "Street": "123 Main St", "City": "Seattle", "State": "WA", "ZipCode": "98101" }, "Contact": { "Email": "alice@company.com", "Phone": "+1-555-0123" }, "Skills@odata.type": "#Collection(String)", "Skills": ["Java", "Spring", "OData", "SQL"] } ] }
案例3: 版本差異處理
public class VersionHandlingExample { public void compareVersions() throws SerializerException, IOException { OData odata = OData.newInstance(); // 創(chuàng)建測(cè)試數(shù)據(jù) Entity product = new Entity(); product.addProperty(new Property(null, "ProductId", ValueType.PRIMITIVE, 1)); product.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Laptop")); product.addProperty(new Property(null, "Price", ValueType.PRIMITIVE, 999.99)); product.addProperty(new Property(null, "InStock", ValueType.PRIMITIVE, true)); EntityCollection products = new EntityCollection(); products.getEntities().add(product); // v4.0 序列化 List<String> versionsV40 = Arrays.asList("4.0"); EdmAssistedSerializer serializerV40 = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON, versionsV40); SerializerResult resultV40 = serializerV40.entityCollection( null, null, products, null); String jsonV40 = IOUtils.toString(resultV40.getContent(), StandardCharsets.UTF_8); System.out.println("OData v4.0 Output:"); System.out.println(jsonV40); // v4.01 序列化 List<String> versionsV401 = Arrays.asList("4.01"); EdmAssistedSerializer serializerV401 = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON, versionsV401); SerializerResult resultV401 = serializerV401.entityCollection( null, null, products, null); String jsonV401 = IOUtils.toString(resultV401.getContent(), StandardCharsets.UTF_8); System.out.println("\nOData v4.01 Output:"); System.out.println(jsonV401); } }
案例4: 元數(shù)據(jù)級(jí)別對(duì)比
public class MetadataLevelExample { public void compareMetadataLevels() throws SerializerException, IOException { OData odata = OData.newInstance(); // 創(chuàng)建測(cè)試數(shù)據(jù) Entity order = new Entity(); order.addProperty(new Property(null, "OrderId", ValueType.PRIMITIVE, 12345)); order.addProperty(new Property(null, "CustomerName", ValueType.PRIMITIVE, "John Smith")); order.addProperty(new Property(null, "OrderDate", ValueType.PRIMITIVE, Calendar.getInstance())); order.addProperty(new Property(null, "TotalAmount", ValueType.PRIMITIVE, 129.99)); EntityCollection orders = new EntityCollection(); orders.getEntities().add(order); ContextURL contextURL = ContextURL.with() .entitySet("Orders") .build(); EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with() .contextURL(contextURL) .build(); // 1. 無(wú)元數(shù)據(jù) EdmAssistedSerializer noMetadata = odata.createEdmAssistedSerializer( ContentType.JSON_NO_METADATA); SerializerResult resultNoMeta = noMetadata.entityCollection( null, null, orders, options); String jsonNoMeta = IOUtils.toString(resultNoMeta.getContent(), StandardCharsets.UTF_8); System.out.println("No Metadata:"); System.out.println(jsonNoMeta); // 2. 最小元數(shù)據(jù) EdmAssistedSerializer minimalMetadata = odata.createEdmAssistedSerializer( ContentType.JSON); SerializerResult resultMinimal = minimalMetadata.entityCollection( null, null, orders, options); String jsonMinimal = IOUtils.toString(resultMinimal.getContent(), StandardCharsets.UTF_8); System.out.println("\nMinimal Metadata:"); System.out.println(jsonMinimal); // 3. 完整元數(shù)據(jù) EdmAssistedSerializer fullMetadata = odata.createEdmAssistedSerializer( ContentType.JSON_FULL_METADATA); SerializerResult resultFull = fullMetadata.entityCollection( null, null, orders, options); String jsonFull = IOUtils.toString(resultFull.getContent(), StandardCharsets.UTF_8); System.out.println("\nFull Metadata:"); System.out.println(jsonFull); } }
案例5: 錯(cuò)誤處理和邊界情況
public class ErrorHandlingExample { public void demonstrateErrorHandling() { OData odata = OData.newInstance(); // 1. 不支持的內(nèi)容類型 try { EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_XML); // 不支持 XML } catch (SerializerException e) { System.out.println("Expected error - Unsupported format: " + e.getMessage()); } // 2. 空數(shù)據(jù)處理 try { EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); EntityCollection emptyCollection = new EntityCollection(); SerializerResult result = serializer.entityCollection( null, null, emptyCollection, null); String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); System.out.println("Empty collection result: " + json); } catch (Exception e) { System.out.println("Error handling empty collection: " + e.getMessage()); } // 3. 空值屬性處理 try { EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); Entity entityWithNulls = new Entity(); entityWithNulls.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Test")); entityWithNulls.addProperty(new Property(null, "NullValue", ValueType.PRIMITIVE, null)); entityWithNulls.addProperty(new Property(null, "EmptyString", ValueType.PRIMITIVE, "")); EntityCollection collection = new EntityCollection(); collection.getEntities().add(entityWithNulls); SerializerResult result = serializer.entityCollection( null, null, collection, null); String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8); System.out.println("Null values handling: " + json); } catch (Exception e) { System.out.println("Error handling null values: " + e.getMessage()); } } }
最佳實(shí)踐
1. 選擇合適的元數(shù)據(jù)級(jí)別
public class MetadataBestPractices { // 生產(chǎn)環(huán)境 - 使用最小元數(shù)據(jù)以減少帶寬 public EdmAssistedSerializer createProductionSerializer() throws SerializerException { OData odata = OData.newInstance(); return odata.createEdmAssistedSerializer(ContentType.JSON); } // 開(kāi)發(fā)和調(diào)試 - 使用完整元數(shù)據(jù)便于調(diào)試 public EdmAssistedSerializer createDevelopmentSerializer() throws SerializerException { OData odata = OData.newInstance(); return odata.createEdmAssistedSerializer(ContentType.JSON_FULL_METADATA); } // 性能敏感場(chǎng)景 - 使用無(wú)元數(shù)據(jù) public EdmAssistedSerializer createPerformanceSerializer() throws SerializerException { OData odata = OData.newInstance(); return odata.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA); } }
2. 版本管理策略
public class VersionManagementBestPractices { private final OData odata = OData.newInstance(); // 支持多版本的通用方法 public EdmAssistedSerializer createVersionAwareSerializer( ContentType contentType, String clientVersion) throws SerializerException { List<String> supportedVersions = determineSupportedVersions(clientVersion); return odata.createEdmAssistedSerializer(contentType, supportedVersions); } private List<String> determineSupportedVersions(String clientVersion) { List<String> versions = new ArrayList<>(); if (clientVersion != null && clientVersion.startsWith("4.01")) { versions.add("4.01"); versions.add("4.0"); // 向后兼容 } else { versions.add("4.0"); } return versions; } }
3. 性能優(yōu)化
public class PerformanceOptimization { // 序列化器復(fù)用 private final Map<String, EdmAssistedSerializer> serializerCache = new ConcurrentHashMap<>(); private final OData odata = OData.newInstance(); public EdmAssistedSerializer getCachedSerializer(ContentType contentType, List<String> versions) throws SerializerException { String key = contentType.toContentTypeString() + "_" + (versions != null ? String.join(",", versions) : "default"); return serializerCache.computeIfAbsent(key, k -> { try { return versions != null && !versions.isEmpty() ? odata.createEdmAssistedSerializer(contentType, versions) : odata.createEdmAssistedSerializer(contentType); } catch (SerializerException e) { throw new RuntimeException("Failed to create serializer", e); } }); } // 批量序列化優(yōu)化 public String serializeLargeDataset(List<Entity> entities, String entitySetName) throws SerializerException, IOException { EdmAssistedSerializer serializer = getCachedSerializer( ContentType.JSON_NO_METADATA, null); // 分批處理大數(shù)據(jù)集 int batchSize = 1000; StringBuilder result = new StringBuilder(); result.append("{\"value\":["); for (int i = 0; i < entities.size(); i += batchSize) { int endIndex = Math.min(i + batchSize, entities.size()); List<Entity> batch = entities.subList(i, endIndex); EntityCollection batchCollection = new EntityCollection(); batchCollection.getEntities().addAll(batch); SerializerResult batchResult = serializer.entityCollection( null, null, batchCollection, null); String batchJson = IOUtils.toString(batchResult.getContent(), StandardCharsets.UTF_8); // 提取值數(shù)組部分 if (i > 0) result.append(","); // 處理批次JSON... } result.append("]}"); return result.toString(); } }
4. 類型安全
public class TypeSafetyBestPractices { // 使用類型安全的屬性創(chuàng)建器 public static class PropertyBuilder { public static Property createStringProperty(String name, String value) { return new Property(null, name, ValueType.PRIMITIVE, value); } public static Property createIntProperty(String name, Integer value) { return new Property(null, name, ValueType.PRIMITIVE, value); } public static Property createDoubleProperty(String name, Double value) { return new Property(null, name, ValueType.PRIMITIVE, value); } public static Property createBooleanProperty(String name, Boolean value) { return new Property(null, name, ValueType.PRIMITIVE, value); } public static Property createDateProperty(String name, Date value) { return new Property(null, name, ValueType.PRIMITIVE, value); } public static Property createComplexProperty(String name, ComplexValue value) { return new Property(null, name, ValueType.COMPLEX, value); } public static Property createCollectionProperty(String name, Collection<?> value) { return new Property(null, name, ValueType.COLLECTION_PRIMITIVE, value); } } // 使用示例 public Entity createTypeSafeEntity() { Entity entity = new Entity(); entity.addProperty(PropertyBuilder.createStringProperty("Name", "John Doe")); entity.addProperty(PropertyBuilder.createIntProperty("Age", 30)); entity.addProperty(PropertyBuilder.createDoubleProperty("Salary", 75000.0)); entity.addProperty(PropertyBuilder.createBooleanProperty("IsActive", true)); entity.addProperty(PropertyBuilder.createDateProperty("HireDate", new Date())); return entity; } }
常見(jiàn)問(wèn)題
Q1: EDM 輔助序列化器與標(biāo)準(zhǔn)序列化器的性能差異有多大?
A: 在大多數(shù)場(chǎng)景下,性能差異在 10-20% 之間。主要開(kāi)銷(xiāo)來(lái)自運(yùn)行時(shí)類型推斷。
public class PerformanceComparison { @Test public void comparePerformance() throws Exception { // 準(zhǔn)備測(cè)試數(shù)據(jù) EntityCollection testData = createLargeTestDataset(10000); // 測(cè)試標(biāo)準(zhǔn)序列化器 long startTime = System.currentTimeMillis(); ODataSerializer standardSerializer = odata.createSerializer(ContentType.APPLICATION_JSON); // ... 序列化邏輯 long standardTime = System.currentTimeMillis() - startTime; // 測(cè)試 EDM 輔助序列化器 startTime = System.currentTimeMillis(); EdmAssistedSerializer assistedSerializer = odata.createEdmAssistedSerializer( ContentType.APPLICATION_JSON); // ... 序列化邏輯 long assistedTime = System.currentTimeMillis() - startTime; System.out.println("Standard serializer: " + standardTime + "ms"); System.out.println("Assisted serializer: " + assistedTime + "ms"); System.out.println("Performance ratio: " + ((double)assistedTime / standardTime)); } }
Q2: 如何處理循環(huán)引用?
A: EDM 輔助序列化器不會(huì)自動(dòng)處理循環(huán)引用,需要在創(chuàng)建數(shù)據(jù)時(shí)避免。
public class CircularReferenceHandling { public Entity createEntityWithoutCircularRef(User user, Set<String> processedIds) { if (processedIds.contains(user.getId())) { // 創(chuàng)建引用實(shí)體,避免循環(huán) Entity refEntity = new Entity(); refEntity.addProperty(PropertyBuilder.createStringProperty("Id", user.getId())); refEntity.addProperty(PropertyBuilder.createStringProperty("Name", user.getName())); return refEntity; } processedIds.add(user.getId()); Entity entity = new Entity(); entity.addProperty(PropertyBuilder.createStringProperty("Id", user.getId())); entity.addProperty(PropertyBuilder.createStringProperty("Name", user.getName())); // 安全地添加關(guān)聯(lián)實(shí)體 if (user.getManager() != null) { Entity managerEntity = createEntityWithoutCircularRef(user.getManager(), processedIds); entity.addProperty(new Property(null, "Manager", ValueType.COMPLEX, convertEntityToComplexValue(managerEntity))); } return entity; } }
Q3: 如何優(yōu)化大數(shù)據(jù)集的序列化?
A: 使用流式處理和分批序列化:
public class LargeDatasetOptimization { public void streamLargeDataset(Iterator<Entity> entityIterator, OutputStream outputStream) throws IOException, SerializerException { JsonGenerator jsonGenerator = new JsonFactory().createGenerator(outputStream); jsonGenerator.writeStartObject(); jsonGenerator.writeArrayFieldStart("value"); EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer( ContentType.JSON_NO_METADATA); while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); EntityCollection singleEntityCollection = new EntityCollection(); singleEntityCollection.getEntities().add(entity); SerializerResult result = serializer.entityCollection( null, null, singleEntityCollection, null); // 直接寫(xiě)入流,避免內(nèi)存積累 IOUtils.copy(result.getContent(), outputStream); if (entityIterator.hasNext()) { jsonGenerator.writeRaw(","); } } jsonGenerator.writeEndArray(); jsonGenerator.writeEndObject(); jsonGenerator.close(); } }
總結(jié)
EDM 輔助序列化器的價(jià)值
- 開(kāi)發(fā)效率: 無(wú)需預(yù)先定義完整的 EDM 模型,可以快速開(kāi)始開(kāi)發(fā)
- 靈活性: 能夠處理動(dòng)態(tài)結(jié)構(gòu)的數(shù)據(jù),適應(yīng)數(shù)據(jù)模型的變化
- 集成友好: 便于與外部系統(tǒng)集成,處理格式不統(tǒng)一的數(shù)據(jù)
- 原型開(kāi)發(fā): 適合快速原型開(kāi)發(fā)和概念驗(yàn)證
適用場(chǎng)景總結(jié)
場(chǎng)景 | 適用性 | 推薦理由 |
---|---|---|
快速原型開(kāi)發(fā) | ????? | 無(wú)需預(yù)定義 EDM,快速驗(yàn)證想法 |
動(dòng)態(tài)數(shù)據(jù)源 | ????? | 能夠處理結(jié)構(gòu)變化的數(shù)據(jù) |
數(shù)據(jù)集成 | ???? | 統(tǒng)一不同格式的數(shù)據(jù)輸出 |
微服務(wù)聚合 | ???? | 整合多個(gè)服務(wù)的異構(gòu)數(shù)據(jù) |
生產(chǎn)環(huán)境 | ??? | 性能略低,但提供更大靈活性 |
最終建議
- 開(kāi)發(fā)階段: 優(yōu)先使用 EDM 輔助序列化器,加快開(kāi)發(fā)速度
- 生產(chǎn)環(huán)境: 如果數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,考慮遷移到標(biāo)準(zhǔn)序列化器以獲得更好性能
- 混合使用: 對(duì)于不同的接口,可以根據(jù)需求選擇不同的序列化器
- 漸進(jìn)式采用: 從 EDM 輔助序列化器開(kāi)始,逐步完善 EDM 模型
EDM 輔助序列化器是 Apache Olingo OData 框架中的一個(gè)強(qiáng)大工具,它在保持 OData 協(xié)議兼容性的同時(shí),提供了極大的開(kāi)發(fā)靈活性。通過(guò)合理使用,可以顯著提高開(kāi)發(fā)效率并簡(jiǎn)化數(shù)據(jù)集成工作。
到此這篇關(guān)于Olingo分析和實(shí)踐之EDM 輔助序列化器詳解(最佳實(shí)踐)的文章就介紹到這了,更多相關(guān)Olingo EDM 輔助序列化器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Swing中的文本區(qū)(JTextArea)實(shí)現(xiàn)換行保存到文件的幾個(gè)方法
這篇文章主要介紹了Java Swing中的文本區(qū)(JTextArea)實(shí)現(xiàn)換行保存到文件的幾個(gè)方法,本文給出了4種方法,需要的朋友可以參考下2014-10-10詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式
在前后端分離的項(xiàng)目中后端返回的格式一定要友好,不然會(huì)對(duì)前端的開(kāi)發(fā)人員帶來(lái)很多的工作量。那么SpringBoot如何做到統(tǒng)一的后端返回格式呢?本文將為大家詳細(xì)講講2022-04-04java8 List<Object>去掉重復(fù)對(duì)象的幾種方法
本文主要介紹了java8 List<Object>去掉重復(fù)對(duì)象的幾種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04SpringMVC用JsonSerialize日期轉(zhuǎn)換方法
下面小編就為大家?guī)?lái)一篇SpringMVC用JsonSerialize日期轉(zhuǎn)換方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起 小編過(guò)來(lái)看看吧2016-11-11Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決)
這篇文章主要介紹了Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11