Partilhar via


Personalizar o armazenamento de arquivos e a serialização XML

Quando o usuário salva uma instância, ou modelo, de uma linguagem específica de domínio (DSL) no Visual Studio, um arquivo XML é criado ou atualizado. O arquivo pode ser recarregado para recriar o modelo na Loja.

Você pode personalizar o esquema de serialização ajustando as configurações em Xml Serialization Behavior no DSL Explorer. Há um nó em Xml Serialization Behavior para cada classe, propriedade e relação de domínio. Os relacionamentos estão localizados em suas classes de origem. Há também nós correspondentes às classes de forma, conector e diagrama.

Você também pode escrever código de programa para personalização mais avançada.

Observação

Se você quiser salvar o modelo em um formato específico, mas não precisar recarregá-lo desse formulário, considere usar modelos de texto para gerar saída do modelo, em vez de um esquema de serialização personalizado. Para obter mais informações, consulte Código Gerado a partir de uma linguagem Domain-Specific.

Arquivos de modelo e diagrama

Cada modelo é guardado em dois ficheiros:

  • O arquivo de modelo tem um nome como Model1.mydsl. Ele armazena os elementos e relações do modelo e suas propriedades. A extensão de ficheiro, como .mydsl, é determinada pela propriedade FileExtension do nó Editor na definição DSL.

  • O arquivo de diagrama tem um nome como Model1.mydsl.diagram. Ele armazena as formas, conectores e suas posições, cores, espessuras de linha e outros detalhes da aparência do diagrama. Se o usuário excluir um arquivo .diagram, as informações essenciais no modelo não serão perdidas. Apenas o layout do diagrama é perdido. Quando o arquivo de modelo é aberto, um conjunto padrão de formas e conectores é criado.

Para alterar a extensão de ficheiro de uma DSL

  1. Abra a Definição de DSL. No DSL Explorer, clique no nó Editor.

  2. Na janela Properties, edite a propriedade FileExtension. Não inclua o . inicial da extensão de nome de arquivo.

  3. No Gerenciador de Soluções, altere o nome dos dois arquivos de modelo de item em DslPackage\ProjectItemTemplates. Esses arquivos têm nomes que seguem este formato:

    myDsl.diagram

    myDsl.myDsl

O esquema de serialização padrão

Para criar um exemplo para este tópico, a seguinte definição DSL foi usada.

Diagrama de definição DSL - modelo de árvore genealógica

Este DSL foi usado para criar um modelo que tem a seguinte aparência na tela.

Diagrama de árvore genealógica, caixa de ferramentas e explorador

Este modelo foi salvo e reaberto no editor de texto XML:

<?xml version="1.0" encoding="utf-8"?>
<familyTreeModel xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core" dslVersion="1.0.0.0" Id="f817b728-e920-458e-bb99-98edc469d78f" xmlns="http://schemas.microsoft.com/dsltools/FamilyTree">
  <people>
    <person name="Henry VIII" birthYear="1491" deathYear="1547" age="519">
      <children>
        <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
        <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Mary" />
      </children>
    </person>
    <person name="Elizabeth I" birthYear="1533" deathYear="1603" age="477" />
    <person name="Mary" birthYear="1515" deathYear="1558" age="495" />
  </people>
</familyTreeModel>

Observe os seguintes pontos sobre o modelo serializado:

  • Cada nó XML tem um nome que é o mesmo que um nome de classe de domínio, exceto que a letra inicial é minúscula. Por exemplo, familyTreeModel e person.

  • Propriedades de domínio como Name e BirthYear são serializadas como atributos nos nós XML. Novamente, o caractere inicial do nome da propriedade é convertido em minúsculas.

  • Cada ligação é serializada como um nó XML aninhado dentro da ponta de origem da ligação. O nó tem o mesmo nome que a propriedade de função de origem, mas com uma letra inicial minúscula.

    Por exemplo, na Definição DSL, um papel que é chamado Pessoas é originado na classe FamilyTree. No XML, a função Pessoas é representada por um nó chamado people aninhado dentro do nó familyTreeModel.

  • A extremidade de destino de cada relação de incorporação é serializada como um nó aninhado sob a relação. Por exemplo, o nó people contém vários nós person.

  • A extremidade alvo de cada relação de referência é serializada como um moniker , que codifica uma referência ao elemento alvo.

    Por exemplo, sob um nó person, pode haver uma relação children. Este nó contém apelidos como:

    <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
    

Entenda os Monikers

Monikers são usados para representar referências cruzadas entre diferentes partes do modelo e arquivos de diagrama. Eles também são usados no arquivo .diagram para se referirem a nodos no arquivo de modelo. Existem duas formas de apelido:

  • Os monikers Id citam o GUID do elemento de destino. Por exemplo:

    <personShapeMoniker Id="f79734c0-3da1-4d72-9514-848fa9e75157" />
    
  • Os monikers qualificados de chave identificam o elemento de destino pelo valor de uma propriedade de domínio designada chamada chave do moniker. O apelido do elemento de destino é prefixado com o apelido de seu elemento pai na árvore de relações de incorporação.

    Os exemplos a seguir são retirados de uma DSL na qual há uma classe de domínio chamada Album, que tem uma relação de incorporação com uma classe de domínio chamada Song:

    <albumMoniker title="/My Favorites/Jazz after Teatime" />
    <songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />
    

    Os monikers de chave qualificados são usados se a classe de destino tiver uma propriedade de domínio para a qual a opção Is Moniker Key está definida como true em Xml Serialization Behavior. No exemplo, essa opção é definida para propriedades de domínio chamadas "Title" nas classes de domínio "Album" e "Song".

Os nomes de chave qualificados são mais fáceis de ler do que os de ID. Se você pretende que o XML de seus arquivos de modelo seja legível por humanos, considere o uso de monikers de chave qualificados. No entanto, é possível que o usuário defina mais de um elemento para ter a mesma chave de apelido. Chaves duplicadas podem fazer com que o arquivo não seja recarregado corretamente. Portanto, se você definir uma classe de domínio que é referenciada usando monikers de chave qualificados, você deve considerar maneiras de impedir que o usuário salve um arquivo que tenha monikers duplicados.

Para definir uma classe de domínio a ser referenciada por identificadores de ID

  1. Certifique-se de que Is Moniker Key seja false para cada propriedade de domínio na classe e nas suas classes base.

    1. No DSL Explorer, expanda Xml Serialization Behavior\Class Data\<a classe de domínio>\Element Data.

    2. Verifique se é a chave Moniker está definida como false para cada propriedade de domínio.

    3. Se a classe de domínio tiver uma classe base, repita o procedimento nessa classe.

  2. Defina = true Serialize Id para a classe de domínio.

    Esta propriedade pode ser encontrada em Xml Serialization Behavior.

Para definir uma classe de domínio a ser referenciada por identificadores de chave qualificados

  • Defina Is Moniker Key para uma propriedade de domínio de uma classe de domínio existente. O tipo de imóvel deve ser string.

    1. No DSL Explorer, expanda Xml Serialization Behavior\Class Data\<a classe de domínio>\Element Datae, em seguida, selecione a propriedade de domínio.

    2. Na janela Propriedades, defina Is Moniker Key como true.

  • - ou -

    Crie uma nova classe de domínio usando a ferramenta Named Domain Class.

    Essa ferramenta cria uma nova classe que tem uma propriedade de domínio chamada Name. O é nome do elemento e é chave de moniker propriedades desta propriedade de domínio são inicializadas para true.

  • - ou -

    Crie uma relação de herança da classe de domínio para outra classe que tenha uma propriedade de chave, conhecida como "moniker".

Evite Monikers duplicados

Se utilizar identificadores de chave qualificados, é possível que dois elementos no modelo de um utilizador possam ter o mesmo valor na propriedade de chave. Por exemplo, se a sua DSL tiver uma classe Pessoa que tenha uma propriedade Nome, o utilizador poderá definir os nomes de dois elementos para serem iguais. Embora o modelo pudesse ser salvo no arquivo, ele não seria recarregado corretamente.

Existem vários métodos que ajudam a evitar esta situação:

  • Defina Is Element Name = true para a propriedade de domínio de chave. Selecione a propriedade de domínio no diagrama de definição DSL e defina o valor na janela Propriedades.

    Quando o usuário cria uma nova instância da classe, esse valor faz com que a propriedade de domínio seja automaticamente atribuída a um valor diferente. O comportamento padrão adiciona um número ao final do nome da classe. Isso não impede que o usuário altere o nome para uma duplicata, mas ajuda no caso em que o usuário não define o valor antes de salvar o modelo.

  • Habilite a validação para a DSL. No DSL Explorer, selecione Editor\Validation e defina as propriedades Uses... como true.

    Há um método de validação gerado automaticamente que verifica se há ambiguidades. O método está na categoria de validação Load. Isso garante que o usuário será avisado de que pode não ser possível reabrir o arquivo.

    Para obter mais informações, consulte Validação em uma Domain-Specific Língua.

Caminhos e Qualificadores de Identificadores

Um identificador de chave qualificado termina com a chave do identificador e é prefixado com o identificador de seu pai na árvore de incorporação. Por exemplo, se o apelido de um álbum for:

<albumMoniker title="/My Favorites/Jazz after Teatime" />

Então uma das músicas desse álbum poderia ser:

<songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />

No entanto, se os álbuns forem referenciados por ID em vez disso, os apelidos serão os seguintes:

<albumMoniker Id="77472c3a-9bf9-4085-976a-d97a4745237c" />
<songMoniker title="/77472c3a-9bf9-4085-976a-d97a4745237c/Hot tea" />

Observe que, como um GUID é exclusivo, ele nunca é prefixado pelo apelido de seu pai.

Se você souber que uma determinada propriedade de domínio sempre terá um valor exclusivo dentro de um modelo, poderá definir Is Moniker Qualifier como true para essa propriedade. Isso faz com que ele seja usado como um qualificador, sem usar o apelido do pai. Por exemplo, se definir como Qualificador de Identificador e como Chave de Identificador para a propriedade de domínio 'Título' da classe 'Álbum', o nome ou identificador do modelo não será usado em identificadores para o Álbum e seus filhos incorporados.

<albumMoniker name="Jazz after Teatime" />
<songMoniker title="/Jazz after Teatime/Hot tea" />

Personalizar a estrutura do XML

Para fazer as seguintes personalizações, expanda o nó Comportamento de serialização Xml no DSL Explorer. Em uma classe de domínio, expanda o nó Dados do elemento para ver a lista de propriedades e relações que são originadas nessa classe. Selecione uma relação e ajuste suas opções na janela Propriedades.

  • Defina Omit Element como true para omitir o nó da função de origem, deixando apenas a lista de elementos de destino. Você não deve definir essa opção se houver mais de uma relação entre as classes de origem e de destino.

    <familyTreeModel ...>
      <!-- The following node is omitted by using Omit Element: -->
      <!-- <people> -->
        <person name="Henry VIII" .../>
        <person name="Elizabeth I" .../>
      <!-- </people> -->
    </familyTreeModel>
    
  • Defina Use a Forma Completa para incorporar os nós-alvo em nós que representam as instâncias de relacionamento. Essa opção é definida automaticamente quando você adiciona propriedades de domínio a uma relação de domínio.

    <familyTreeModel ...>
      <people>
        <!-- The following node is inserted by using Use Full Form: -->
        <familyTreeModelHasPeople myRelationshipProperty="x1">
          <person name="Henry VIII" .../>
        </familyTreeModelHasPeople>
        <familyTreeModelHasPeople myRelationshipProperty="x2">
          <person name="Elizabeth I" .../>
        </familyTreeModelHasPeople>
      </people>
    </familyTreeModel>
    
  • Defina Representação = Elemento para que uma propriedade de domínio seja salva como um elemento em vez de como um valor de atributo.

    <person name="Elizabeth I" birthYear="1533">
      <deathYear>1603</deathYear>
    </person>
    
  • Para alterar a ordem em que os atributos e relacionamentos são serializados, clique com o botão direito do mouse em um item em Dados do elemento e use os comandos de menu Mover para cima ou Mover para baixo comando.

Personalização principal usando o código do programa

Você pode substituir partes ou todos os algoritmos de serialização.

Recomendamos que você estude o código em Dsl\Generated Code\Serializer.cs e SerializationHelper.cs.

Para personalizar a serialização de uma classe específica

  1. Defina Is Custom no nó dessa classe em Xml Serialization Behavior.

  2. Transforme todos os modelos, crie a solução e investigue os erros de compilação resultantes. Comentários perto de cada erro explicam qual código você tem que fornecer.

Para fornecer a sua própria serialização para o modelo completo

  1. Sobrescrever métodos em Dsl\GeneratedCode\SerializationHelper.cs

Observação

A partir do Visual Studio 2022 17.13, a implementação de serialização padrão não suporta mais a serialização ou desserialização de tipos de dados personalizados usando BinaryFormatter devido a riscos de segurança com BinaryFormatter.

Se você usar um tipo de dados personalizado para qualquer propriedade de domínio, precisará substituir os métodos de serialização na classe SerializationHelper ou implementar um TypeConverter capaz de converter cada tipo de dados personalizado de e para uma cadeia de caracteres.

Embora não seja recomendável usar BinaryFormatter por motivos de segurança, se você precisar manter a compatibilidade com modelos mais antigos que usavam BinaryFormatter serialização, poderá implementar um TypeConverter que desserialize os dados binários. O trecho de código a seguir serve como um modelo para implementar essa compatibilidade:

class MyCustomDataTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string text)
        {
            // First, try to parse the string as if it were returned by MyCustomDataType.ToString().
            if (MyCustomDataType.TryParse(text, out var custom))
                return custom;

            // Fall back to trying to deserialize the old BinaryFormatter serialization format.
            var decoded = Convert.FromBase64String(text);
            using (var memory = new MemoryStream(decoded, false))
            {
                var binaryFormatter = new BinaryFormatter();
                return binaryFormatter.Deserialize(memory) as MyCustomDataType;
            }
        }

        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string) && value is MyCustomDataType custom)
            return custom.ToString();

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

// ...

[TypeConverter(MyCustomDataTypeConverter)]
class MyCustomDataType
{
    // ...
}

Opções no comportamento da serialização XML

No DSL Explorer, o nó Xml Serialization Behavior contém um nó filho para cada classe de domínio, relacionamento, forma, conector e classe de diagrama. Em cada um desses nós há uma lista de propriedades e relações originadas nesse elemento. As relações são representadas tanto por direito próprio como sob as suas classes de origem.

A tabela a seguir resume as opções que você pode definir nesta seção da Definição DSL. Em cada caso, selecione um elemento no DSL Explorer e defina as opções na janela Propriedades.

Dados da classe Xml

Esses elementos são encontrados no DSL Explorer em Xml Serialization Behavior\Class Data.

Propriedade Descrição
Tem esquema de elemento personalizado Se True, indica que a classe de domínio tem um esquema de elemento personalizado
É personalizado Defina o valor como True se quiser escrever seu próprio código de serialização e desserialização para essa classe de domínio.

Crie a solução e investigue os erros para descobrir instruções detalhadas.
Classe de domínio Classe de domínio à qual este nó de dados da classe se aplica. Somente leitura.
Nome do elemento Nome do nó XML para elementos desta classe. O valor padrão é uma versão minúscula do nome da classe de domínio.
Nome do atributo de identificador Nome do atributo usado em elementos de moniker para conter a referência. Se estiver em branco, o nome da propriedade ou do id da chave será usado.

Neste exemplo, é "nome": <personMoniker name="/Mike Nash"/>
Nome do elemento Moniker Nome do elemento xml usado para monikers que se referem a elementos dessa classe.

O valor padrão é uma versão minúscula do nome da classe acrescido do sufixo "Moniker". Por exemplo, personMoniker.
Nome do tipo de moniker Nome do tipo xsd gerado para monikers para elementos desta classe. O XSD está em Dsl\Generated Code\*Schema.xsd
Serializar ID Se for Verdadeiro, o GUID do elemento é incluído no arquivo. O valor deve ser definido como True se não houver nenhuma propriedade marcada Is Moniker Key e a DSL definir relações de referência para essa classe.
Nome do tipo Nome do tipo xml gerado no xsd da classe de domínio designada.
Observações Notas informais associadas a este elemento

Dados de propriedade XML

Os nós de propriedade Xml são encontrados abaixo dos nós de classe.

Propriedade Descrição
Propriedade do Domínio Propriedade à qual os dados de configuração de serialização xml se aplicam. Somente leitura.
É Moniker Key Se o valor for definido como True, a propriedade será usada como a chave para criar monikers que fazem referência a instâncias dessa classe de domínio.
É Moniker Qualifier Se o valor for definido como True, a propriedade será usada para criar o qualificador em monikers. Se for falso, e se SerializeId não for verdadeiro para esta classe de domínio, os monikers serão qualificados pelo moniker do elemento pai na árvore de aninhamento.
Representação Se o valor for definido como Atributo, a propriedade será serializada como um atributo xml; se o valor for definido como Elemento, ele será serializado como um elemento; se o valor estiver definido como Ignorar, ele não será serializado.
Nome XML Nome usado para o atributo xml ou elemento que representa a propriedade. Por padrão, o valor é uma versão minúscula do nome da propriedade de domínio.
Observações Notas informais associadas a este elemento

Dados da função Xml

Os nós de dados de função são encontrados sob os nós da classe de origem.

Propriedade Descrição
Tem apelido personalizado Defina isso como true se você quiser fornecer seu próprio código para gerar e resolver apelidos que atravessam essa relação.

Para obter instruções detalhadas, crie a solução e clique duas vezes nas mensagens de erro.
Relação de Domínio Especifica a relação à qual essas opções se aplicam. Só leitura.
Omitir elemento Se verdadeiro, o nó XML que corresponde à função de origem é omitido do esquema.

Se houver mais de uma relação entre as classes de origem e de destino, esse nó de função distinguirá entre os links que pertencem às duas relações. Portanto, recomendamos que você não defina essa opção neste caso.
Nome do elemento de função Especifica o nome do elemento XML derivado da função de origem. O valor padrão é o nome da propriedade da função.
Utilizar o formulário completo Se for verdade, cada elemento de destino ou apelido é incluído num nó XML que representa a relação. Isto deve ser definido como verdadeiro se a relação tiver as suas próprias propriedades de domínio.