2009-04-06 9 views
2

Ich habe eine Situation, in der ich eine XML-Datei habe, die ich nicht ändern möchte. Die AddAnnotation-Funktion in der XElement-Klasse bietet eine Option zum Hinzufügen von Nur-Speicher-Daten, die nicht serialisiert und nicht Teil der XML-Datei sind.XML-Serialisierungsanmerkungen

Ich möchte in der Lage sein, diese Anmerkungen zu speichern (zum Beispiel: in eine andere XML-Datei) und dann das XML und die Anmerkungen zu deserialisieren, um das gleiche Objekt zu bekommen.

Ich möchte nicht das Original-XML ändern und das ist der Grund, dass ich Anmerkungen verwende.

Zusammenfassend möchte ich in der Lage sein, benutzerdefinierte Daten zu einer XML-Datei hinzuzufügen. Diese Daten werden nicht Teil des XML sein, wenn ich es serialisiere, oder es wird ein Teil des XML sein, aber ich wäre in der Lage, das ursprüngliche XML einfach wiederzufinden.

Haben Sie irgendeine Empfehlung, wie ich so etwas tun kann?

Edit: Sollte ich XML-Verarbeitungsanweisungen verwenden? Sind Verarbeitungsanweisungen für diese Art der Nutzung vorgesehen?

Antwort

2

Es mir wie der einfachste Ansatz klingt wären regulären Knoten zu verwenden, aber in einem anderen XML-Namensraum - also

<foo standardAttrubute="abc" myData:customAttribute="def"> 
    <standardElement>ghi</standardElement > 
    <myData:customElement>jkl</myData:customElement> 
</foo> 

(wo myData ein xmlns alias für die Namespace-uri ist)

In vielen Fällen suchen Leser nur nach Daten in ihrem-Namespace (oder dem Standard/leeren Namespace) - Werte in benutzerdefinierten Namespaces werden in der Regel übersprungen.

Um das ursprüngliche XML zu packen, wäre ein einfacher Ansatz, es über einen XSLT auszuführen, der nur den standardmäßigen/ursprünglichen Namespace berücksichtigt.

static void Strip(XElement el, XNamespace ns) { 
     List<XElement> remove = new List<XElement>(); 
     foreach (XElement child in el.Elements()) { 
      if (child.Name.Namespace == ns) { 
       remove.Add(child); 
      } else { 
       Strip(child, ns); 
      } 
     } 
     remove.ForEach(child => child.Remove()); 

     foreach (XAttribute child in 
      (from a in el.Attributes() 
      where a.Name.Namespace == ns 
      select a).ToList()) { 
      child.Remove(); 
     } 
    } 
+0

Sounds interessant ... Können Sie ein Beispiel geben (zwei Beispiele eigentlich) wie Ich kann das XML mit XElement laden, mit und ohne das benutzerdefinierte Attribut? –

+0

Creation added - haben Sie das gemeint? Oder etwas anderes? –

+0

Nein. Ich weiß, wie man das XML erstellt. Ich weiß nicht, wie ich es ohne die benutzerdefinierten Daten laden soll. Tnx! –

1

Die ursprüngliche Frage verwendet, um das Wort "Serialize", aber dann auch XElement und Annotation erwähnt:


XNamespace myData = XNamespace.Get("http://mycustomdata/"); 
XElement el = new XElement("foo", 
    new XAttribute(XNamespace.Xmlns + "myData", myData.NamespaceName), 
    new XAttribute("standardAttribute", "abc"), 
    new XAttribute(myData + "customAttribute", "def"), 
    new XElement("standardElement", "ghi"), 
    new XElement(myData + "customAttribute", "jkl")); 
string s = el.ToString(); 

Um solche Daten aus einem XElement, vielleicht zu entfernen. Für mich sind das zwei verschiedene Dinge.

Wenn Sie wirklich verwenden möchten die XmlSerializer:
Was ich tun würde Gebrauch XmlAttributeOverrides ist, die Serialisierung zu unterscheiden. Mit den XmlAttributeOverrides können Sie zur Laufzeit die XML-Serialisierungsattribute, die Ihre Typen dekorieren, zur Laufzeit programmatisch überschreiben.

Innerhalb Ihres Typs können Sie ein Feld/eine Eigenschaft haben, die die Annotation/Dokumentation enthalten soll. Dekoriere das mit XmlIgnore. Erstellen Sie dann eine Instanz des XmlSerializers, der keine Überschreibungen akzeptiert. Die Annotation wird nicht serialisiert oder deinserialisiert. Erstellen Sie eine andere Instanz von XmlSerializer für diesen Typ mithilfe eines XmlAttributeOverrides-Objekts. Geben Sie eine Überschreibung für die Eigenschaft XmlIgnore'd an (verwenden Sie XmlElementAttribute), und überschreiben Sie Attribute für alle anderen Member (verwenden Sie XmlIgnore = true).

Serialisieren Sie die Instanz zweimal, eine mit jedem Serializer.


Edit: hier ist der Code:

public class DTO 
{ 
    [XmlIgnore] 
    public string additionalInformation; 

    [XmlElement(Order=1)] 
    public DateTime stamp; 

    [XmlElement(Order=2)] 
    public string name; 

    [XmlElement(Order=3)] 
    public double value; 

    [XmlElement(Order=4)] 
    public int index; 
} 



public class OverridesDemo 
{ 
    public void Run() 
    { 
     DTO dto = new DTO 
      { 
       additionalInformation = "This will bbe serialized separately", 
       stamp = DateTime.UtcNow, 
       name = "Marley", 
       value = 72.34, 
       index = 7 
      }; 


     // --------------------------------------------------------------- 
     // 1. serialize normally 
     // this will allow us to omit the xmlns:xsi namespace 
     var ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     XmlSerializer s1 = new XmlSerializer(typeof(DTO)); 

     var builder = new System.Text.StringBuilder(); 
     var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true }; 

     Console.WriteLine("\nSerialize using the in-line attributes: "); 
     using (XmlWriter writer = XmlWriter.Create(builder, settings)) 
     { 
      s1.Serialize(writer, dto, ns); 
     } 
     Console.WriteLine("{0}",builder.ToString()); 
     Console.WriteLine("\n");    
     // --------------------------------------------------------------- 

     // --------------------------------------------------------------- 
     // 2. serialize with attribute overrides 
     // use a non-empty default namespace 
     ns = new XmlSerializerNamespaces(); 
     string myns = "urn:www.example.org"; 
     ns.Add("", myns); 

     XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 

     XmlAttributes attrs = new XmlAttributes(); 
     // override the (implicit) XmlRoot attribute 
     XmlRootAttribute attr1 = new XmlRootAttribute 
      { 
       Namespace = myns, 
       ElementName = "DTO-Annotations", 
      }; 
     attrs.XmlRoot = attr1; 

     overrides.Add(typeof(DTO), attrs); 
     // "un-ignore" the first property 
     // define an XmlElement attribute, for a type of "String", with no namespace 
     var a2 = new XmlElementAttribute(typeof(String)) { ElementName="note", Namespace = myns }; 

     // add that XmlElement attribute to the 2nd bunch of attributes 
     attrs = new XmlAttributes(); 
     attrs.XmlElements.Add(a2); 
     attrs.XmlIgnore = false; 

     // add that bunch of attributes to the container for the type, and 
     // specifically apply that bunch to the "additionalInformation" property 
     // on the type. 
     overrides.Add(typeof(DTO), "additionalInformation", attrs); 

     // now, XmlIgnore all the other properties 
     attrs = new XmlAttributes(); 
     attrs.XmlIgnore = true;  
     overrides.Add(typeof(DTO), "stamp", attrs); 
     overrides.Add(typeof(DTO), "name", attrs); 
     overrides.Add(typeof(DTO), "value", attrs); 
     overrides.Add(typeof(DTO), "index", attrs); 

     // create a serializer using those xml attribute overrides 
     XmlSerializer s2 = new XmlSerializer(typeof(DTO), overrides); 

     Console.WriteLine("\nSerialize using the override attributes: "); 
     builder.Length = 0; 
     using (XmlWriter writer = XmlWriter.Create(builder, settings)) 
     { 
      s2.Serialize(writer, dto, ns); 
     } 
     Console.WriteLine("{0}",builder.ToString()); 
     Console.WriteLine("\n");    
     // --------------------------------------------------------------- 
    } 
} 

Ausgang, mit dem in-line-Attribute:

<DTO> 
    <stamp>2009-06-30T02:17:35.918Z</stamp> 
    <name>Marley</name> 
    <value>72.34</value> 
    <index>7</index> 
</DTO> 

Ausgabe, die Überschreibung mit Attributen:

<DTO-Annotations xmlns="urn:www.example.org"> 
    <note>This will bbe serialized separately</note> 
</DTO-Annotations> 
+0

Ich bin mir nicht sicher, dass ich folgte. Können Sie ein Snippet bereitstellen? tnx ... –