2009-10-28 15 views
7

Dato il seguente esempio XML, possiamo immaginare uno schema che definisce Root come contenente una sequenza di numero illimitato di scelte tra Tipo1 e Tipo2.Ordine di conservazione in sequenza di scelte (da LINQ a XSD)

<Root> 
    <Type1 /> 
    <Type2 /> 
    <Type2 /> 
    <Type1 /> 
</Root> 

sto testando fuori la migrazione dallo strumento XSD.exe che anche se aggiunge tipo di sicurezza ha un sacco di piccoli fastidi. Lo strumento XSD in questo caso crea semplicemente all'interno di Root un array di tipo System.Object e devi capire quale tipo di oggetti (Tipo1 o Tipo2) ci sono dentro. Non è completamente elegante, ma almeno preservi l'ordine.

Il problema è quando LINQ a XSD crea gli oggetti, definisce Root come dotato di due elenchi indipendenti di Tipo1 e Tipo2. Questo è fantastico in quanto è sicuro per i tipi, ma ora sembra che perda l'ordine degli elementi. Ho sviluppato LINQ to XSD dall'origine su codeplex.

Utilizzando LINQ to XSD, come posso mantenere l'ordine di questi elementi?

+3

Bene, hai già presentato le uniche due opzioni. O ottieni una raccolta debolmente tipizzata che conserva l'ordine o ottieni una raccolta fortemente tipizzata per tipo. Fai finta di non utilizzare affatto XML - come scriveresti un oggetto di puro codice che ha una singola raccolta fortemente tipizzata con più tipi al suo interno? –

+0

La mia domanda è come conservare l'ordine degli elementi in questo scenario usando Linq a XSD. Mi rendo conto di avere una collezione di tipi misti che dovrebbero essere di System.Object (o qualsiasi genitore che hanno in comune). Sono disposto a rinunciare a oggetti fortemente tipizzati in questo scenario w/Linq a XSD per preservare l'ordine. Speravo ci fosse un modo per costringerlo a farlo. Nel mio caso d'uso, l'ordine è importante, quindi non potrei usare Linq per XSD, anche se mi piacerebbe molto visto che ha molti vantaggi rispetto a XSD.exe – Philip

+6

Puoi farlo con l'ereditarietà. Se sia Type1 che Type2 avevano una classe base comune, potresti avere un IList . Ora hai una singola lista fortemente tipizzata e l'ordine è preservato.Quando si scorre l'elenco, è sufficiente controllare il tipo dell'oggetto corrente. foreach (BaseType el negli elementi) { se (el è Type1) ... else if (el è tipo2) ...} –

risposta

2

Come creare un wrapper attorno a Choice? Limitare i tipi che si accede in questo modo:

class Choice 
{ 
    private object _value; 

    public ChoiceEnum CurrentType { get; private set; } 

    public Type1 Type1Value 
    { 
     get { return (Type1) _value; } 
     set { _value = value; CurrentType = ChoiceEnum.Type1; } 
    } 

    public Type2 Type2Value 
    { 
     get { return (Type2) _value; } 
     set { _value = value; CurrentType = ChoiceEnum.Type2; } 
    } 
} 

Questa è una versione semplificata, e si dovrà aggiungere più di validazione (se _value è di tipo corretto, qual è l'attuale tipo di _value, ecc).

Quindi, è possibile filtrare con LINQ:

var q1 = from v in root.Sequence 
     where v.CurrentType == ChoiceEnum.Type1 
     select v.Type1; 

Oppure è possibile creare metodi in radice che vi avvolgerà le query.

+0

Buon suggerimento. Questo dovrebbe funzionare, ma determina il design dello stesso XSD, che è sfortunato e talvolta impossibile (ad esempio gli schemi standard del settore). Molti degli schemi con cui lavoriamo sul nostro team vengono creati da noi, quindi potremmo essere in grado di utilizzare questa tecnica. – Philip

1

Linq2Xsd esegue il trip sulle sequenze solo quando esiste un elemento xsd: choice.

Fortunatamente sono stato in grado di rimuovere xsd: scelta per il Amazon XSD che sto usando (non usavo MerchantOrderID), che ha permesso di conservare correttamente la sequenza nel file ToString() per xml.

  <xsd:choice>        <--- removed line 
       <xsd:element ref="AmazonOrderID"/> 
       <xsd:element ref="MerchantOrderID"/> <--- removed line 
      </xsd:choice>        <--- removed line 

      <xsd:element name="ActionType" minOccurs="0" maxOccurs="1"> 
       <xsd:simpleType> 
        <xsd:restriction base="xsd:string"> 
         <xsd:enumeration value="Refund"/> 
         <xsd:enumeration value="Cancel"/> 
        </xsd:restriction> 
       </xsd:simpleType> 
      </xsd:element> 

Il codice generato poi ha correttamente questo nel costruttore che conserva l'ordine

contentModel = new SequenceContentModelEntity(
       new NamedContentModelEntity(XName.Get("AmazonOrderID", "")), 
       new NamedContentModelEntity(XName.Get("ActionType", "")), 
       new NamedContentModelEntity(XName.Get("CODCollectionMethod", "")), 
       new NamedContentModelEntity(XName.Get("AdjustedItem", ""))); 

Si può anche essere in grado di farlo manualmente sottoclasse YOUSELF, ma sono non sono sicuro di come questo potrebbe funzionare con una xsd: scelta. Questo è described here ma non l'ho provato.