Сериализация пар имя / значение в пользовательском объекте через веб-службу

это очень сложный вопрос о том, как сериализовать данные через вызов веб-службы, когда данные не типизированы. Я постараюсь изложить все как можно точнее.

Образец Объекта Хранения:

[Serializable]
public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public List<NameValuePairs> OtherInfo { get; set; }  
}
[Serializable]   
public class NameValuePairs {
  public string Name { get; set; }
  public string Value { get; set; }
}

Пример Использования:

[WebMethod]
    public List<StorageObject> GetStorageObjects() {
      List<StorageObject> o = new List<StorageObject>() {
        new StorageObject() { 
          Name = "Matthew",
          Birthday = "Jan 1st, 2008", 
          OtherInfo = new List<NameValuePairs>() {
            new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
            new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
          }
        },
        new StorageObject() { 
          Name = "Joe",
          Birthday = "Jan 10th, 2008",
          OtherInfo = new List<NameValuePairs>() { 
            new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
            new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
          }
        }
      };

      return o;
    }

возвращаемое значение от веб-службы:

<StorageObject>
    <Name>Matthew</Name>
    <Birthday>Jan 1st, 2008</Birthday>
    <OtherInfo>
        <NameValuePairs>
            <Name>Hobbies</Name>
            <Value>Programming</Value>
        </NameValuePairs>
        <NameValuePairs>
            <Name>Website</Name>
            <Value>Stackoverflow.com</Value>
        </NameValuePairs>
    </OtherInfo>
</StorageObject>

что хочется:

<OtherInfo>
    <Hobbies>Programming</Hobbies>
    <Website>Stackoverflow.com</Website>
</OtherInfo>

Причина & Другие Вещи:

во-первых, я сожалею о длине сообщения, но я также хотел дать воспроизводимый код.

Я хочу его в этом формате, потому что я потребляю веб-службы из PHP. Я хочу легко пойти:

/ / ЭТО IMPORANT

In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".  

если это в другом формате, то для меня не было бы никакого способа сделать это вообще. Кроме того, в C# , если я потребляю услугу, я также хотел бы иметь возможность сделать следующий:

/ / ЭТО IMPORANT

In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];

к сожалению, я не уверен, как это сделать. Я смог получить его таким образом, создав пользовательский словарь, который реализовал IXmlSerializer (см. StackOverflow: Словарь IXmlSerializer), однако, он взорвал схему WSDL из воды. Это также слишком сложно и привело к ужасным результатам в моем приложении WinFormsTester!

есть ли способ сделать это ? Какой тип объекта нужно создать ? Есть ли способ сделать это /кроме как сделать строго типизированную коллекцию/ ? Очевидно, если я сделаю это сильно набранным следующим образом:

public class OtherInfo {
  public string Hobbies { get; set; }
  public string FavoriteWebsite { get; set; }
}

тогда он будет работать отлично, у меня не будет проблем с WSDL, я смогу легко получить доступ к нему из PHP и C# (.OtherInfo.Хобби.)

тем не менее, я бы полностью потерял точку NVP, в том, что я должен был бы заранее знать, что такое список, и он был бы неизменным.. скажем, из базы данных.

спасибо всем!! Надеюсь, мы сможем найти какое-то решение. Вот требования снова:

  1. схема WSDL не должна ломаться
  2. пары значений имен (NVP) должны быть сериализованы в формат атрибута
  3. должно быть легко получить доступ к NVP в PHP по имени ["хобби"]
  4. должно быть легко получить доступ в C# (и быть совместимым с его генератором Прокси)
  5. будет легко сериализуемый
  6. не требуйте от меня строго вводить данные

теперь я / полностью / открыт для ввода на лучший / другой способ сделать это. Я храню некоторую относительно "статическую" информацию (например, имя) и кучу фрагментов данных. Если есть лучший способ, я с удовольствием послушаю.

5 ответов


это похоже на динамические свойства объекта. C# не совсем динамический язык в отличие от javascript или, возможно, PHP может анализировать свойства объекта на лету. Следующие два метода - это то, что я могу придумать. Второй может соответствовать вашим требованиям.

путь поцелуя

держать его простым глупым способом

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public List<string> OtherInfo { get; set; }  
}

вы можете иметь пары значений имен, которые разделены на'/'

OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}

сериализовать формы

<StorageObject>
    <Name>Matthew</Name>
    <Birthday>Jan 1st, 2008</Birthday>
    <OtherInfo>
        <string>Hobbies|Programming</string>
        <string>Website|Stackoverflow.com</string>
    </OtherInfo>
</StorageObject>

динамический путь в C#

сделайте часть пары значений имен элементом XML, чтобы вы могли создавать ее динамически.

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}

вы можете легко создать объект OtherInfo как элемент, ориентированный например,

XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);

сериализованная форма будет

<OtherInfo>
    <Hobbies>Programming</Hobbies>
    <Website>Stackoverflow.com</Website>
</OtherInfo>

или построить его как атрибут centric

XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);

<OtherInfo>
    <nvp n="Hobbies" v="Programming" />
    <nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>

для любого динамического языка он может напрямую обращаться к свойствам. В остальном, они могут получить доступ к значению, прочитав XML. Чтение XML хорошо поддерживается большинством framework.


вот на чем я остановился.

Классовую Структуру:

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  [XmlAnyElement("Info")]  // this prevents double-nodes in the XML
  public XElement OtherInfo { get; set; }
}

использование:

StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");

выход:

<Info>
  <Hobbies>Programming</Hobbies>
  <Website>Stackoverflow.com</Website>
</Info>

Я хотел бы поблагодарить всех за их помощь, я действительно ценю помощь и идеи.


как совершенно другой подход к этому, почему бы не подумать о том, чтобы сделать это совершенно по-другому. Есть один метод веб-службы для возврата сериализованного объекта хранения, минус OtherInfo и другой метод для возврата списка свойств (ключей) для OtherInfo, и третий, чтобы вернуть список значений для любого ключа. Конечно, потребуется больше поездок туда и обратно в веб-сервис, если вы хотите получить все данные, но решение будет намного проще и гибче.

[Serializable]
public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }

  [Nonserializable]
  public Dictionary<string,List<string>> OtherInfo { get; set; }  
}

[WebMethod]
public List<StorageObject> GetStorageObjects() {
    // returns list of storage objects from persistent storage or cache
}

[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
    // find storage object, sObj
    return sObj.Keys.ToList();
}

[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
    // find storage object, sObj
    return sObj[attribute];
}

посмотрите в систему.XML.Сериализация.Атрибут XmlSerializerAssemblyAttribute. Это позволяет указать пользовательский сериализатор уровня класса. Вы сможете выплюнуть любой XML, который вам нравится.

быстрый способ получить до скорости на них, чтобы использовать сгэн.exe для генерации одного и взглянуть на него с отражателем.

- Oisin


Я не уверен, что это решит вашу проблему (это было бы в C#, но, возможно, не в PHP), но попробуйте использовать Dictionary<string,List<string>> OtherInfo вместо List<NameValuePairs>. Тогда "хобби" и "веб-сайты" будут вашими ключами, а ценности-списком хобби или веб-сайтов. Хотя я не уверен, как это будет сериализовано.

вы сможете ссылаться на списки хобби как:

List<string> hobbies = storageObject.OtherInfo["Hobbies"];

[EDIT] см.здесь для универсального XML-сериализуемого словаря. Этот производный класс является единственным вам нужно будет использовать вместо универсального словаря.