# AUR4100

## Error Message

`AUR4100: Unable to deserialize the expression: {{expression}}`

Where `{{expression}}` is the serialized expression that could not be deserialized.

## Description

This error occurs when the validation system cannot deserialize a previously serialized validation expression. This typically happens when:

1. The serialized expression format is corrupted or invalid
2. The expression was serialized with a different version of Aurelia validation
3. The serialization contains references to functions or objects that no longer exist
4. Manual manipulation of serialized validation data resulted in invalid format

## Common Scenarios

### Corrupted Serialized Data

```typescript
// ❌ Problem: Corrupted or manually edited serialized validation data
const corruptedRules = {
  "propertyName": "userName",
  "expression": "{invalid serialized data}"
};

// Attempting to deserialize this will throw AUR4100
validator.hydrateRules(corruptedRules);
```

### Version Mismatch

```typescript
// ❌ Problem: Rules serialized with older Aurelia version
const oldVersionRules = {
  // Rules from Aurelia 1.x or incompatible format
  "rules": "old_format_that_cannot_be_parsed"
};
```

### Missing Function References

```typescript
// ❌ Problem: Serialized expression references functions that no longer exist
class MyValidator {
  customValidationFunction(value: any) {
    return value.length > 0;
  }
}

// If serialized rules reference customValidationFunction but the class
// structure changed, deserialization may fail
```

## Solutions

### 1. **Regenerate Validation Rules**

The most reliable solution is to recreate the validation rules from scratch:

```typescript
import { ValidationRules } from '@aurelia/validation';

export class User {
  userName: string = '';
  email: string = '';
  
  constructor() {
    // ✅ Correct: Define rules programmatically instead of deserializing
    ValidationRules
      .ensure((u: User) => u.userName)
        .required()
        .minLength(3)
        .maxLength(50)
      .ensure((u: User) => u.email)
        .required()
        .email()
      .on(User);
  }
}
```

### 2. **Validate Serialized Data Before Deserialization**

```typescript
import { IValidationRules } from '@aurelia/validation';

export class ValidationManager {
  
  safeDeserializeRules(serializedRules: any): boolean {
    try {
      // ✅ Correct: Validate structure before attempting deserialization
      if (!this.isValidSerializedFormat(serializedRules)) {
        console.warn('Invalid serialized validation format, using defaults');
        return false;
      }
      
      // Attempt deserialization
      this.validationRules.hydrateRules(serializedRules);
      return true;
    } catch (error) {
      console.error('Failed to deserialize validation rules:', error);
      return false;
    }
  }
  
  private isValidSerializedFormat(data: any): boolean {
    // Basic validation of serialized format
    return data && 
           typeof data === 'object' && 
           Array.isArray(data.rules) &&
           data.rules.every(rule => 
             typeof rule.property === 'string' &&
             typeof rule.displayName === 'string'
           );
  }
}
```

### 3. **Handle Migration from Old Versions**

```typescript
export class ValidationMigrator {
  migrateRules(oldRules: any): ValidationRules {
    // ✅ Correct: Convert old format to new format
    const newRules = ValidationRules.create();
    
    if (this.isOldFormat(oldRules)) {
      // Convert old format to new programmatic rules
      this.convertLegacyRules(oldRules, newRules);
    }
    
    return newRules;
  }
  
  private isOldFormat(rules: any): boolean {
    // Check if rules are in old Aurelia 1.x format
    return rules && rules.version && rules.version.startsWith('1.');
  }
  
  private convertLegacyRules(oldRules: any, newRules: ValidationRules) {
    // Implementation specific to your old rule format
    oldRules.properties?.forEach(prop => {
      let ruleBuilder = newRules.ensure(prop.name);
      
      prop.rules?.forEach(rule => {
        switch (rule.type) {
          case 'required':
            ruleBuilder = ruleBuilder.required();
            break;
          case 'minLength':
            ruleBuilder = ruleBuilder.minLength(rule.value);
            break;
          // Add other rule conversions as needed
        }
      });
    });
  }
}
```

### 4. **Implement Fallback Strategy**

```typescript
export class RobustValidationSetup {
  
  async setupValidation(model: any, serializedRules?: string) {
    try {
      if (serializedRules) {
        // ✅ Correct: Try deserialization with fallback
        const parsed = JSON.parse(serializedRules);
        await this.validationRules.hydrateRules(parsed);
        return;
      }
    } catch (error) {
      console.warn('Could not deserialize validation rules, using defaults:', error);
    }
    
    // Fallback to default rules
    this.setupDefaultValidation(model);
  }
  
  private setupDefaultValidation(model: any) {
    // Define fallback validation rules
    if (model.constructor.name === 'User') {
      ValidationRules
        .ensure('userName').required().minLength(3)
        .ensure('email').required().email()
        .on(model.constructor);
    }
  }
}
```

## Example: Complete Solution

```typescript
// validation-manager.ts
import { ValidationRules, IValidationRules } from '@aurelia/validation';

export class ValidationManager {
  constructor(private validationRules: IValidationRules) {}
  
  async loadValidationRules(modelClass: any, serializedData?: string) {
    if (serializedData) {
      try {
        const rules = JSON.parse(serializedData);
        
        // Validate the structure first
        if (this.isValidRuleFormat(rules)) {
          this.validationRules.hydrateRules(rules);
          return true;
        }
      } catch (error) {
        console.warn('Failed to deserialize validation rules:', error);
      }
    }
    
    // Fallback to programmatic rules
    this.createDefaultRules(modelClass);
    return false;
  }
  
  private isValidRuleFormat(rules: any): boolean {
    return rules && 
           typeof rules === 'object' &&
           (!rules.rules || Array.isArray(rules.rules));
  }
  
  private createDefaultRules(modelClass: any) {
    // Create type-safe validation rules
    switch (modelClass.name) {
      case 'User':
        ValidationRules
          .ensure((u: User) => u.userName).required().minLength(3)
          .ensure((u: User) => u.email).required().email()
          .on(modelClass);
        break;
      // Add other model types as needed
    }
  }
}
```

## Debugging Tips

1. **Check JSON Format**: Ensure serialized validation data is valid JSON
2. **Validate Structure**: Check that required properties exist in serialized data
3. **Version Compatibility**: Ensure serialized rules are compatible with current Aurelia version
4. **Use Fallbacks**: Always have default validation rules as fallback
5. **Log Errors**: Log deserialization errors for debugging

## Related Errors

* [AUR4104](https://docs.aurelia.io/developer-guides/error-messages/4100-to-4106/aur4104) - Hydrate rule not an array
* [AUR4105](https://docs.aurelia.io/developer-guides/error-messages/4100-to-4106/aur4105) - Hydrate rule unsupported
* [AUR4106](https://docs.aurelia.io/developer-guides/error-messages/4100-to-4106/aur4106) - Hydrate rule invalid name
