2015-10-02 13 views
10

Ho un sacco di classi C#, che sono generate automaticamente da un XSD. Quindi creo file XML basati su quelle classi C#. Nulla di esistente finora.Posso avere attributo nullo e altro attributo nello stesso tag in XML creato dalla classe generata da XSD C#?

Il problema:

I file XML generati stanno attraversando la convalida e la convalida richiede un attributo in più per tutti i tag XML con xsi:nil="true". Fondamentalmente i tag dovrebbero assomigliare a: <testTag.01 xsi:nil="true" NV="123123" />, ma non posso ottenerlo in C#. Il mio codice è:

 if (myObject.TestTag.HasValue) 
     { 
      t.testTag01 = new testTag01(); 
      t.testTag01.Value = myObject.TestTag.Value; 
     } 
     //else 
     //{ 
     // t.testTag01 = new testTag01(); 
     // t.testTag01.NV = "123123";//Not Recorded 
     //} 

Questo codice genera <testTag.01>SomeValue</testTag.01> o <testTag.01 xsi:nil="true"/>.

Se io rimuovere il commento dalla ELSE, il risultato sarebbe: <testTag.01>SomeValue</testTag.01> o <testTag.01 NV="123123" />.

Quindi non ho idea di come arrivare al formato, che è richiesto dallo strumento di convalida. Qualche idea ?

P.S.

Ecco l'auto-generato C# classe:

/// [System.CodeDom.Compiler.GeneratedCodeAttribute ("XSD", "4.0.30319.33440")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute ("codice")] [System.Xml.Serialization.XmlTypeAttribute (AnonymousType = true, Namespace = "http://www.blabla.org ")]

public partial class testTag01 {

private string nvField; 

private SomeEnum valueField; 

/// <remarks/> 
[System.Xml.Serialization.XmlAttributeAttribute()] 
public string NV { 
    get { 
     return this.nvField; 
    } 
    set { 
     this.nvField = value; 
    } 
} 

/// <remarks/> 
[System.Xml.Serialization.XmlTextAttribute()] 
public SomeEnum Value { 
    get { 
     return this.valueField; 
    } 
    set { 
     this.valueField = value; 
    } 
} } 

non vorrei alterare quella parte, ma capisco che è impossibile senza farlo. Inoltre ho provato a impostare SomeEnum come Nullable. public SomeEnum? Value, ma è un'eccezione:

Cannot serialize member 'Value' of type System.Nullable`1[]. XmlAttribute/XmlText cannot be used to encode complex types. 
+0

È necessario visualizzare le classi generate automaticamente. Questi di solito dovevano essere modificati. Non è necessario aggiungere codice per produrre valori nulli. – jdweng

+0

Ho messo la classe. Inoltre ho provato a impostare il valore su Nullable, ma sta generando un'eccezione. :-( – Tech0

+1

Questo non funzionerà fuori dalla scatola. Vedi [** Xsi: nil Supporto Binding Attributo **: L'attributo nil e altri attributi] (https://msdn.microsoft.com/en-us/library/ ybce7f69.aspx). – dbc

risposta

1

XmlSerializer non supporta direttamente vincolante per gli elementi che hanno contemporaneamente xsi:nil="true" insieme ad altri valori di attributo; vedi Xsi:nil Attribute Binding Support: The nil attribute and other attributes.

Pertanto, è necessario emettere manualmente l'attributo.

Se si vuole essere in grado di generare un elemento senza contenuto e due attributi, uno di nome NV e l'altro sempre essere xsi:nil="true", è possibile modificare la classe testTag01 di avere la proprietà NV così come una proprietà di sintesi avendo lo spazio dei nomi e il nome corretto:

public class testTag01 
{ 
    [XmlAttribute] 
    public string NV { get; set; } 

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")] 
    public string Nil { get { return "true"; } set { } } 
} 

Se volte vogliono avere xsi:nil="true" ma altre volte vogliono l'elemento di avere contenuti che corrisponde al vostro SomeEnum, è necessario fare qualcosa di un bi t più complicato, poiché la xsi:nil="true" deve essere soppressa quando l'elemento ha contenuto:

public class testTag01 
{ 
    [XmlAttribute] 
    public string NV { get; set; } 

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")] 
    public string Nil { get { return SomeEnum == null ? "true" : null; } set { } } 

    public bool ShouldSerializeNil() { return SomeEnum == null; } 

    [XmlIgnore] 
    public SomeEnum? SomeEnum { get; set; } 

    [XmlText] 
    public string SomeEnumText 
    { 
     get 
     { 
      if (SomeEnum == null) 
       return null; 
      return SomeEnum.Value.ToString(); 
     } 
     set 
     { 
      // See here if one needs to parse XmlEnumAttribute attributes 
      // http://stackoverflow.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value 
      value = value.Trim(); 
      if (string.IsNullOrEmpty(value)) 
       SomeEnum = null; 
      else 
      { 
       try 
       { 
        SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, false); 
       } 
       catch (Exception) 
       { 
        SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, true); 
       } 
      } 
     } 
    } 
} 

(un elemento che ha contemporaneamente sia xsi:nil="true" e il contenuto sarebbe una violazione della XML standard; si spera non si dispone di questo)

quindi utilizzarlo come:.

public class TestClass 
{ 
    [XmlElement("testTag.01")] 
    public testTag01 TestTag { get; set; } 

    public static void Test() 
    { 
     Test(new TestClass { TestTag = new testTag01 { NV = "123123" } }); 
     Test(new TestClass { TestTag = new testTag01 { NV = "123123", SomeEnum = SomeEnum.SomeValue } }); 
    } 

    private static void Test(TestClass test) 
    { 
     var xml = test.GetXml(); 

     var test2 = xml.LoadFromXML<TestClass>(); 

     Console.WriteLine(test2.GetXml()); 
     Debug.WriteLine(test2.GetXml()); 

     if (test2.TestTag.NV != test.TestTag.NV) 
     { 
      throw new InvalidOperationException("test2.TestTag.NV != test.TestTag.NV"); 
     } 
    } 
} 

L'output XML assomiglia:

<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <testTag.01 NV="123123" xsi:nil="true" /> 
</TestClass> 

O

<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <testTag.01 NV="123123">SomeValue</testTag.01> 
</TestClass> 

Prototype fiddle usando questi metodi di estensione:

public static class XmlSerializationHelper 
{ 
    public static T LoadFromXML<T>(this string xmlString, XmlSerializer serializer = null) 
    { 
     T returnValue = default(T); 

     using (StringReader reader = new StringReader(xmlString)) 
     { 
      object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader); 
      if (result is T) 
      { 
       returnValue = (T)result; 
      } 
     } 
     return returnValue; 
    } 

    public static string GetXml<T>(this T obj, XmlSerializerNamespaces ns = null, XmlWriterSettings settings = null, XmlSerializer serializer = null) 
    { 
     using (var textWriter = new StringWriter()) 
     { 
      settings = settings ?? new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes. 
      using (var xmlWriter = XmlWriter.Create(textWriter, settings)) 
       (serializer ?? new XmlSerializer(typeof(T))).Serialize(xmlWriter, obj, ns); 
      return textWriter.ToString(); 
     } 
    } 
} 
0

Come previsto non c'è alcuna soluzione per questo caso, fuori dalla scatola, così ho improvvisare un po 'e ha raggiunto il mio obiettivo in una logica post-elaborazione.

Sto analizzando l'XML generato e se sto cercando un nodo con attributo xsi: nil, ma senza attributo NV: aggiungo l'attributo NV con il valore predefinito. Lo stesso per i nodi con attributo NV, ma non xsi: nil.

Ecco il codice:

 XmlDocument doc = new XmlDocument();// instantiate XmlDocument and load XML from file 
     doc.Load("somepath.xml"); 

     //Get the nodes with NV attribute(using XPath) and add xsi:nill to that nodes 
     XmlNodeList nodes = doc.SelectNodes("//*[@NV]"); 

     foreach (XmlNode node in nodes) 
     { 
      XmlAttribute nilAttr = doc.CreateAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance"); 
      nilAttr.Value = "true"; 
      node.Attributes.Append(nilAttr); 
     } 

     //Get the nodes with xsi:nill attribute(using XPath) and add NV with default value to that nodes 
     XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable); 
     nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); 
     XmlNodeList nilNodes = doc.SelectNodes("//*[@xsi:nil]", nsManager); 

     foreach (XmlNode node in nilNodes) 
     { 
      XmlAttribute nvAttr = doc.CreateAttribute("NV"); 
      nvAttr.Value = "7701003"; 
      node.Attributes.Append(nvAttr); 
     } 

     doc.Save("somepath.xml"); 

La risposta superiore rende totalmente senso, ma dal momento che queste classi sono generati automaticamente lo farò la mia strada con la post-elaborazione, perche 'se il provider cambia lo schema XSD, la mia soluzione non ha bisogno di alcun lavoro extra. Grazie comunque.

+1

In realtà, a seconda delle versioni del framework, ecc. essere in grado di emettere la classe generata come parziale, quindi è possibile includere "l'attributo sempre nullo" dall'alto in un file di seconda classe. –

Problemi correlati