XML 序列化参考 - 重复
本文关键字:重复 参考 序列化 XML | 更新日期: 2023-09-27 18:35:37
[Java or C#] 我在序列化方面遇到了一些问题。如何不复制有关对象的所有信息并仅使用引用?
示例类:
class Author {
public String id;
public String name;
}
class Book {
public String id;
public Author author;
public String title;
}
我必须像下面这样格式化输出文件:
<store>
<authors>
<author id="PK">
<name>Philip Kindred</name>
</author>
</authors>
<books>
<book id="u1">
<author>PK</author> <!-- use only ID -->
<title>Ubik</title>
</book>
</books>
</store>
您面临着在 XML 中表示聚合而不是组合关系的问题。当您保留父子关系(即组合)时,XML 序列化非常简单。在这种情况下,一本书有一个(或更多)作者,但不拥有它,因为一个作者可以是许多其他书籍的作者。
在这种情况下,您可以执行类似于在数据库中执行的操作,即具有两个单独的条目并通过外键表示关系。请参阅以下示例:
[Serializable]
public class Author
{
[XmlAttribute("id")]
public String Id { get; set; }
[XmlElement("name")]
public String Name { get; set; }
}
[Serializable]
public class Book
{
private Author _author;
[XmlIgnore]
public Author Author
{
get { return _author; }
set
{
_author = value;
AuthorId = _author != null ? _author.Id : null;
}
}
[XmlAttribute("id")]
public String Id { get; set; }
[XmlElement("author")]
public String AuthorId { get; set; }
[XmlElement("title")]
public String Title { get; set; }
}
[Serializable]
public class Store
{
[XmlArray("authors")]
[XmlArrayItem("author", Type = typeof(Author))]
public List<Author> Authors { get; set; }
[XmlArray("books")]
[XmlArrayItem("book", Type = typeof(Book))]
public List<Book> Books { get; set; }
public Store()
{
Books = new List<Book>();
Authors = new List<Author>();
}
}
internal class Program
{
private static void Main(string[] args)
{
// Create some authors
var authors = new List<Author>
{
new Author{Id="PK", Name="Philip Kindred"},
new Author{Id="WS", Name="William Shakespeare"},
};
// Create some books linked to the authors
var books = new List<Book>
{
new Book{Id = "U1", Author = authors[0], Title = "Do Androids Dream of Electric Sheep?"},
new Book{Id = "U2", Author = authors[1], Title = "Romeo and Juliet"}
};
var store = new Store {Authors = authors, Books = books};
var success = Serialiser.SerialiseToXml(store, "store.xml");
// Deserialize the data from XML
var store2 = Serialiser.DeserialseFromXml<Store>("store.xml");
// Resolve the actual Author instances from the saved IDs (foreign key equivalent in databases)
foreach (var book in store2.Books)
book.Author = store2.Authors.FirstOrDefault(author => author.Id == book.AuthorId);
// Now variable 'store' and 'store2' have the same equivalent data
}
}
// Helper class to serialize and deserialize the data to XML file
public static class Serialiser
{
public static bool SerialiseToXml(object obj, string filename)
{
try
{
var ws = new XmlWriterSettings
{
NewLineHandling = NewLineHandling.Entitize,
NewLineChars = Environment.NewLine,
Indent = true,
NewLineOnAttributes = false
};
var xs = new XmlSerializer(obj.GetType());
using (var writer = XmlWriter.Create(filename, ws))
xs.Serialize(writer, obj);
return true;
}
catch(Exception ex)
{
return false;
}
}
public static T DeserialseFromXml<T>(string filename) where T : new()
{
var typeofT = typeof(T);
try
{
var xs = new XmlSerializer(typeofT);
using (var reader = XmlReader.Create(filename))
return (T)xs.Deserialize(reader);
}
catch(Exception ex)
{
return default(T);
}
}
}
"商店.xml"将如下所示:
<?xml version="1.0" encoding="utf-8"?>
<Store xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<authors>
<author id="PK">
<name>Philip Kindred</name>
</author>
<author id="WS">
<name>William Shakespeare</name>
</author>
</authors>
<books>
<book id="U1">
<author>PK</author>
<title>Do Androids Dream of Electric Sheep?</title>
</book>
<book id="U2">
<author>WS</author>
<title>Romeo and Juliet</title>
</book>
</books>
</Store>
C# 答案
如果您有一些较大的容器类可以管理对象之间的交叉引用,则可以执行此操作。 在您的情况下,您似乎有一个可以达到此目的的Store
对象。 Store
按姓名维护书籍和作者的词典;Book
会记住其作者的id
,并根据需要从商店中获取实际Author
。 当然,这需要Author
和Book
都知道它们所在的商店。
示例实现可能如下所示:
public class Author
{
string id;
Store store;
[XmlIgnore]
public Store Store {
get {
return store;
}
set {
if (store != null && id != null)
store.Authors.Remove(id);
this.store = value;
if (store != null && id != null)
store.Authors[id] = this;
}
}
[XmlAttribute("id")]
public string Id
{
get
{
return id;
}
set
{
if (store != null && id != null)
store.Authors.Remove(id);
this.id = value;
if (store != null && id != null)
store.Authors[id] = this;
}
}
[XmlElement("name")]
public string Name { get; set; }
}
public class Book
{
string authorId;
string id;
Store store;
[XmlIgnore]
public Store Store
{
get
{
return store;
}
set
{
if (store != null && id != null)
store.Books.Remove(id);
this.store = value;
if (store != null && id != null)
store.Books[id] = this;
}
}
[XmlAttribute("id")]
public string Id
{
get
{
return id;
}
set
{
if (store != null && id != null)
store.Books.Remove(id);
this.id = value;
if (store != null && id != null)
store.Books[id] = this;
}
}
[XmlElement("author")]
public string AuthorID
{
get
{
return authorId;
}
set
{
authorId = value;
}
}
[XmlIgnore]
public Author Author
{
get
{
if (store == null)
return null;
if (AuthorID == null)
return null;
return store.Authors[AuthorID];
}
set
{
if (value == Author)
return;
if (value == null)
{
authorId = null;
}
else
{
if (value.Id == null)
throw new ArgumentException();
authorId = value.Id;
}
AssertCorrectAuthor(value);
}
}
[Conditional("DEBUG")]
private void AssertCorrectAuthor(Author author)
{
if (store != null)
Debug.Assert(author == Author);
}
[XmlElement("title")]
public string Title { get; set; }
}
[XmlRoot("store")]
public class Store
{
readonly Dictionary<string, Book> books = new Dictionary<string, Book>();
readonly Dictionary<string, Author> authors = new Dictionary<string, Author>();
[XmlIgnore]
public IDictionary<string, Book> Books
{
get
{
return books;
}
}
[XmlIgnore]
public IDictionary<string, Author> Authors
{
get
{
return authors;
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[XmlArray("authors")]
[XmlArrayItem("author")]
public Author[] AuthorList // proxy array for serialization.
{
get
{
return Authors.Values.ToArray();
}
set
{
foreach (var author in authors.Values)
{
author.Store = null;
}
Authors.Clear();
if (value == null)
return;
foreach (var author in value)
{
author.Store = this;
}
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[XmlArray("books")]
[XmlArrayItem("book")]
public Book[] BookList // proxy array for serialization.
{
get
{
return Books.Values.ToArray();
}
set
{
foreach (var book in Books.Values)
{
book.Store = null;
}
Books.Clear();
if (value == null)
return;
foreach (var book in value)
{
book.Store = this;
}
}
}
}
并且,要测试:
public static class TestStore
{
public static void Test()
{
string xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<store>
<authors>
<author id=""PK"">
<name>Philip Kindred</name>
</author>
</authors>
<books>
<book id=""u1"">
<author>PK</author> <!-- use only ID -->
<title>Ubik</title>
</book>
<book id=""t1"">
<author>PK</author> <!-- use only ID -->
<title>The Transmigration of Timothy Archer</title>
</book>
</books>
</store>
";
var store = xml.LoadFromXML<Store>();
Debug.Assert(store.BookList[0].Author == store.AuthorList[0]); // no assert
Debug.Assert(store.BookList[1].Author == store.AuthorList[0]); // no assert; verify that all books use the same instance of the `Author` class.
}
}